Contacts Provider est un composant Android puissant et flexible qui gère le dépôt central de données sur les personnes de l'appareil. Le fournisseur de contacts est la source des données que vous voyez dans l'application Contacts de l'appareil. Vous pouvez également accéder à ses données dans votre propre application et les transférer entre l'appareil et les services en ligne. Le fournisseur gère un large éventail de sources de données et essaie de gérer autant de données que possible pour chaque personne, ce qui rend son organisation complexe. C'est pourquoi l'API du fournisseur inclut un vaste ensemble de classes et d'interfaces de contrat qui facilitent la récupération et la modification des données.
Ce guide décrit les éléments suivants:
- Structure de base du fournisseur
- Comment récupérer des données auprès du fournisseur
- Modifier les données du fournisseur
- Écrire un adaptateur de synchronisation pour synchroniser les données de votre serveur vers le fournisseur de contacts
Dans ce guide, nous partons du principe que vous connaissez les principes de base des fournisseurs de contenu Android. Pour en savoir plus sur les fournisseurs de contenu Android, consultez le guide Principes de base des fournisseurs de contenu.
Organisation du fournisseur de contacts
Contacts Provider est un composant de fournisseur de contenu Android. Elle gère trois types de données sur une personne, chacune correspondant à une table proposée par le fournisseur, comme illustré dans la figure 1:
![](https://developer.android.google.cn/static/images/providers/contacts_structure.png?authuser=19&hl=fr)
Figure 1 : Structure de la table du fournisseur de contacts.
Les trois tableaux sont communément appelés par les noms de leurs classes de contrat. Les classes définissent des constantes pour les URI de contenu, les noms de colonne et les valeurs de colonne utilisées par les tables:
-
ContactsContract.Contacts
table - Lignes représentant différentes personnes, sur la base des agrégations de lignes de contact brutes.
-
ContactsContract.RawContacts
table - Lignes contenant un résumé des données d'une personne, spécifiques à un compte utilisateur et à un type.
-
ContactsContract.Data
table - Lignes contenant les détails du contact brut, tels que les adresses e-mail ou les numéros de téléphone.
Les autres tables représentées par des classes de contrat dans ContactsContract
sont des tables auxiliaires utilisées par le fournisseur de contacts pour gérer ses opérations ou prendre en charge des fonctions spécifiques dans les contacts ou les applications de téléphonie de l'appareil.
Contacts bruts
Un contact brut représente les données d'une personne provenant d'un seul type de compte et d'un seul nom de compte. Étant donné que le fournisseur de contacts autorise plusieurs services en ligne comme source de données pour une personne, il autorise plusieurs contacts bruts pour la même personne. Plusieurs contacts bruts permettent également à un utilisateur de combiner les données d'une personne provenant de plusieurs comptes du même type de compte.
La plupart des données d'un contact brut ne sont pas stockées dans la table ContactsContract.RawContacts
. Au lieu de cela, il est stocké dans une ou plusieurs lignes de la table ContactsContract.Data
. Chaque ligne de données comporte une colonne Data.RAW_CONTACT_ID
contenant la valeur RawContacts._ID
de la ligne ContactsContract.RawContacts
parente.
Colonnes brutes importantes concernant les contacts
Les colonnes importantes de la table ContactsContract.RawContacts
sont répertoriées dans le tableau 1. Veuillez lire les remarques qui suivent après le tableau:
Tableau 1. Colonnes de contacts brutes importantes.
Nom de la colonne | Utiliser | Notes |
---|---|---|
ACCOUNT_NAME
|
Nom du type de compte qui est la source de ce contact brut.
Par exemple, le nom d'un compte Google est l'une des adresses Gmail du propriétaire de l'appareil. Pour plus d'informations, consultez la section suivante sur ACCOUNT_TYPE .
|
Le format de ce nom est spécifique au type de compte associé. Il ne s'agit pas nécessairement d'une adresse e-mail. |
ACCOUNT_TYPE
|
Type de compte qui est la source de ce contact brut. Par exemple, le type de compte d'un compte Google est com.google . Complétez toujours votre type de compte avec un identifiant de domaine pour un domaine que vous possédez ou contrôlez. Votre compte sera ainsi unique.
|
Un type de compte qui fournit des données de contacts est généralement associé à un adaptateur de synchronisation qui se synchronise avec Contacts Provider. |
DELETED
|
Indicateur "supprimé" pour un contact brut | Cette option permet au fournisseur de contacts de gérer la ligne en interne jusqu'à ce que les adaptateurs de synchronisation puissent la supprimer de leurs serveurs, puis la supprimer du dépôt. |
Notes
Voici quelques remarques importantes concernant la table ContactsContract.RawContacts
:
-
Le nom d'un contact brut n'est pas stocké dans sa ligne dans
ContactsContract.RawContacts
. Au lieu de cela, il est stocké dans la tableContactsContract.Data
, sur une ligneContactsContract.CommonDataKinds.StructuredName
. Un contact brut ne possède qu'une seule ligne de ce type dans la tableContactsContract.Data
. -
Attention:Pour utiliser vos propres données de compte dans une ligne de contact brute, vous devez d'abord les enregistrer auprès de
AccountManager
. Pour ce faire, invitez les utilisateurs à ajouter le type et le nom de leur compte à la liste des comptes. Si vous ne le faites pas, le fournisseur de contacts supprimera automatiquement votre ligne de contacts bruts.Par exemple, si vous souhaitez que votre application conserve les données des contacts de votre service Web avec le domaine
com.example.dataservice
et que le compte de l'utilisateur pour votre service estbecky.sharp@dataservice.example.com
, celui-ci doit d'abord ajouter le "type" (com.example.dataservice
) et le nom du compte (becky.smart@dataservice.example.com
) avant que votre application puisse ajouter des lignes de contacts brutes. Vous pouvez expliquer cette exigence à l'utilisateur dans la documentation, ou lui demander d'ajouter le type et le nom, ou les deux. Les types et les noms de comptes sont décrits plus en détail dans la section suivante.
Sources des données brutes sur les contacts
Pour comprendre le fonctionnement des contacts bruts, prenons l'exemple de l'utilisatrice "Emily Dickinson", qui a défini les trois comptes utilisateur suivants sur son appareil:
emily.dickinson@gmail.com
emilyd@gmail.com
- Compte Twitter "belle_of_amherst"
Cet utilisateur a activé la synchronisation des contacts pour ces trois comptes dans les paramètres Accounts.
Supposons qu'Emily Dickinson ouvre une fenêtre de navigateur, se connecte à Gmail en tant que emily.dickinson@gmail.com
, ouvre Contacts et ajoute "Thomas Higginson". Par la suite, elle se connecte à Gmail en tant que emilyd@gmail.com
et envoie un e-mail à "Thomas Higginson", qui l'ajoute automatiquement en tant que contact. Elle suit également "colonel_tom" (ID Twitter de Thomas Higginson) sur Twitter.
Suite à ce travail, le fournisseur de contacts crée trois contacts bruts:
-
Contact brut pour "Thomas Higginson" associé à
emily.dickinson@gmail.com
. Le type de compte utilisateur est Google. -
Un deuxième contact brut pour "Thomas Higginson" associé à
emilyd@gmail.com
. Le type de compte utilisateur est également Google. Il existe un deuxième contact brut, même si le nom est identique à l'ancien, car la personne a été ajoutée pour un autre compte utilisateur. - Un troisième contact brut pour "Thomas Higginson" associé à "belle_of_amherst". Le type de compte utilisateur est Twitter.
Données
Comme indiqué précédemment, les données d'un contact brut sont stockées dans une ligne ContactsContract.Data
associée à la valeur _ID
du contact brut. Un même contact brut peut ainsi avoir plusieurs instances du même type de données, telles que des adresses e-mail ou des numéros de téléphone. Par exemple, si "Thomas Higginson" pour emilyd@gmail.com
(la ligne de contacts bruts de Thomas Higginson associée au compte Google emilyd@gmail.com
) possède une adresse e-mail personnelle thigg@gmail.com
et une adresse e-mail professionnelle thomas.higginson@gmail.com
, le fournisseur de contacts stocke les deux lignes d'adresse e-mail et les associe au contact brut.
Notez que différents types de données sont stockés dans cette seule table. Les lignes concernant le nom à afficher, le numéro de téléphone, l'adresse e-mail, l'adresse postale, la photo et les détails du site Web se trouvent dans la table ContactsContract.Data
. Pour vous aider à gérer ce mode, la table ContactsContract.Data
comporte certaines colonnes avec des noms descriptifs et d'autres avec des noms génériques. Le contenu d'une colonne "nom descriptif" a la même signification quel que soit le type de données de la ligne, tandis que le contenu d'une colonne "nom générique" a différentes significations selon le type de données.
Noms de colonnes descriptifs
Voici quelques exemples de noms de colonnes descriptifs:
-
RAW_CONTACT_ID
-
Valeur de la colonne
_ID
du contact brut pour ces données. -
MIMETYPE
-
Type des données stockées dans cette ligne, exprimé sous la forme d'un type MIME personnalisé. Le fournisseur de contacts utilise les types MIME définis dans les sous-classes de
ContactsContract.CommonDataKinds
. Ces types MIME sont Open Source et peuvent être utilisés par toute application ou tout adaptateur de synchronisation fonctionnant avec le fournisseur de contacts. -
IS_PRIMARY
-
Si ce type de ligne de données peut apparaître plusieurs fois pour un contact brut, la colonne
IS_PRIMARY
signale la ligne de données qui contient les données principales du type. Par exemple, si l'utilisateur appuie de manière prolongée sur le numéro de téléphone d'un contact et sélectionne Définir par défaut, la colonneIS_PRIMARY
de la ligneContactsContract.Data
contenant ce numéro est définie sur une valeur non nulle.
Noms de colonnes génériques
15 colonnes génériques nommées DATA1
à DATA15
sont en disponibilité générale et quatre colonnes génériques supplémentaires (SYNC1
à SYNC4
) qui ne doivent être utilisées que par les adaptateurs de synchronisation. Les constantes des noms de colonne génériques fonctionnent toujours, quel que soit le type de données contenues dans la ligne.
La colonne DATA1
est indexée. Le fournisseur de contacts utilise toujours cette colonne pour les données qui, selon le fournisseur, seront la cible la plus fréquente d'une requête. Par exemple, dans une ligne d'e-mail, cette colonne contient l'adresse e-mail réelle.
Par convention, la colonne DATA15
est réservée au stockage de données BLOB (Binary Large Object), telles que les vignettes de photos.
Noms de colonnes spécifiques au type
Pour faciliter l'utilisation des colonnes pour un type de ligne particulier, Contacts Provider fournit également des constantes de nom de colonne spécifiques au type, définies dans les sous-classes de ContactsContract.CommonDataKinds
. Les constantes affectent simplement le même nom de colonne sous un autre nom de constante, ce qui vous permet d'accéder aux données d'une ligne d'un type particulier.
Par exemple, la classe ContactsContract.CommonDataKinds.Email
définit des constantes de nom de colonne spécifiques au type pour une ligne ContactsContract.Data
de type MIME Email.CONTENT_ITEM_TYPE
. La classe contient la constante ADDRESS
pour la colonne d'adresse e-mail. La valeur réelle de ADDRESS
est "data1", qui est identique au nom générique de la colonne.
Attention:N'ajoutez pas vos propres données personnalisées à la table ContactsContract.Data
à l'aide d'une ligne associée à l'un des types MIME prédéfinis par le fournisseur. Sinon, vous risquez de perdre les données ou de provoquer un dysfonctionnement du fournisseur. Par exemple, vous ne devez pas ajouter de ligne avec le type MIME Email.CONTENT_ITEM_TYPE
contenant un nom d'utilisateur au lieu d'une adresse e-mail dans la colonne DATA1
. Si vous utilisez votre propre type MIME personnalisé pour la ligne, vous êtes libre de définir vos propres noms de colonnes spécifiques au type et d'utiliser les colonnes comme vous le souhaitez.
La figure 2 montre comment les colonnes descriptives et les colonnes de données apparaissent dans une ligne ContactsContract.Data
, et comment les noms de colonnes spécifiques au type "superposent" les noms de colonnes génériques
![Correspondance entre les noms de colonnes spécifiques au type et les noms de colonnes génériques](https://developer.android.google.cn/static/images/providers/data_columns.png?authuser=19&hl=fr)
Figure 2. Noms de colonnes spécifiques au type et noms de colonnes génériques.
Classes de noms de colonnes spécifiques au type
Le tableau 2 liste les classes de noms de colonnes spécifiques au type les plus couramment utilisées:
Tableau 2. Classes de noms de colonnes spécifiques au type
Classe Mapping | Type de données | Notes |
---|---|---|
ContactsContract.CommonDataKinds.StructuredName |
Données de nom du contact brut associé à cette ligne de données. | Un contact brut ne comporte qu'une seule de ces lignes. |
ContactsContract.CommonDataKinds.Photo |
Photo principale du contact brut associé à cette ligne de données. | Un contact brut ne comporte qu'une seule de ces lignes. |
ContactsContract.CommonDataKinds.Email |
Adresse e-mail du contact brut associé à cette ligne de données. | Un contact brut peut avoir plusieurs adresses e-mail. |
ContactsContract.CommonDataKinds.StructuredPostal |
Adresse postale du contact brut associé à cette ligne de données. | Un contact brut peut avoir plusieurs adresses postales. |
ContactsContract.CommonDataKinds.GroupMembership |
Identifiant qui associe le contact brut à l'un des groupes du fournisseur de contacts. | Les groupes sont une fonctionnalité facultative d'un type de compte et d'un nom de compte. Ils sont décrits plus en détail dans la section Groupes de contacts. |
Contacts
Le fournisseur de contacts combine les lignes de contacts brutes de tous les types et noms de comptes pour former un contact. Cela facilite l'affichage et la modification de toutes les données qu'un utilisateur a collectées à son sujet. Le fournisseur de contacts gère la création de nouvelles lignes de contacts et l'agrégation des contacts bruts avec une ligne de contact existante. Ni les applications ni les adaptateurs de synchronisation ne sont autorisés à ajouter des contacts, et certaines colonnes d'une ligne de contacts sont en lecture seule.
Remarque:Si vous essayez d'ajouter un contact au fournisseur de contacts avec un insert()
, vous obtiendrez une exception UnsupportedOperationException
. Si vous essayez de mettre à jour une colonne répertoriée comme "lecture seule", la mise à jour est ignorée.
Contacts Provider crée un contact en réponse à l'ajout d'un nouveau contact brut qui ne correspond à aucun contact existant. Le fournisseur procède également si les données d'un contact brut existant changent et ne correspondent plus au contact auquel il était précédemment associé. Si une application ou un adaptateur de synchronisation crée un contact brut correspondant à un contact existant, ce nouveau contact brut est agrégé au contact existant.
Le fournisseur de contacts associe une ligne de contact à ses lignes de contact brutes à la colonne _ID
de la ligne de contact dans la table Contacts
. La colonne CONTACT_ID
de la table de contacts bruts ContactsContract.RawContacts
contient des valeurs _ID
pour la ligne de contacts associée à chaque ligne de contacts bruts.
La table ContactsContract.Contacts
comporte également la colonne LOOKUP_KEY
, qui est un lien "permanent" vers la ligne de contact. Étant donné que le fournisseur de contacts gère les contacts automatiquement, il peut modifier la valeur _ID
d'une ligne de contact en réponse à une agrégation ou à une synchronisation. Même dans ce cas, l'URI de contenu CONTENT_LOOKUP_URI
combiné au LOOKUP_KEY
du contact pointe toujours vers la ligne du contact. Vous pouvez donc utiliser LOOKUP_KEY
pour conserver les liens vers les contacts "favoris", et ainsi de suite. Cette colonne a son propre format qui n'est pas lié à celui de la colonne _ID
.
La figure 3 montre comment les trois tableaux principaux sont liés les uns aux autres.
![Tableaux principaux du fournisseur de contacts](https://developer.android.google.cn/static/images/providers/contacts_tables.png?authuser=19&hl=fr)
Figure 3. Relations entre les tables Contacts, Contacts bruts et Détails.
Attention : Si vous publiez votre application sur le Google Play Store, ou si elle est installée sur un appareil équipé d'Android 10 (niveau d'API 29) ou version ultérieure, n'oubliez pas qu'un ensemble limité de champs et de méthodes de données des contacts est obsolète.
Dans les conditions mentionnées, le système efface régulièrement toutes les valeurs écrites dans ces champs de données:
-
ContactsContract.ContactOptionsColumns.LAST_TIME_CONTACTED
-
ContactsContract.ContactOptionsColumns.TIMES_CONTACTED
-
ContactsContract.DataUsageStatColumns.LAST_TIME_USED
-
ContactsContract.DataUsageStatColumns.TIMES_USED
Les API utilisées pour définir les champs de données ci-dessus sont également obsolètes:
En outre, les champs suivants n'affichent plus les contacts fréquents. Notez que certains de ces champs n'influencent le classement des contacts que lorsque ceux-ci font partie d'un type de données spécifique.
-
ContactsContract.Contacts.CONTENT_FREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_URI
-
ContactsContract.Contacts.CONTENT_STREQUENT_FILTER_URI
-
CONTENT_FILTER_URI
(ne concerne que les types de données Email, Phone, Callable et Contactables) -
ENTERPRISE_CONTENT_FILTER_URI
(affecte uniquement les genres de données Email, Phone et Callable)
Si vos applications accèdent à ces champs ou API, ou les mettent à jour, utilisez d'autres méthodes. Par exemple, vous pouvez répondre à certains cas d'utilisation en faisant appel à des fournisseurs de contenu privé ou à d'autres données stockées dans vos applications ou systèmes de backend.
Pour vérifier que la fonctionnalité de votre application n'est pas affectée par cette modification, vous pouvez effacer manuellement ces champs de données. Pour ce faire, exécutez la commande ADB suivante sur un appareil équipé d'Android 4.1 (niveau d'API 16) ou version ultérieure:
adb shell content delete \ --uri content://com.android.contacts/contacts/delete_usage
Données provenant d'adaptateurs de synchronisation
Les utilisateurs saisissent les données des contacts directement dans l'appareil, mais elles sont également transmises au fournisseur de contacts à partir des services Web via des adaptateurs de synchronisation, qui automatisent le transfert de données entre l'appareil et les services. Les adaptateurs de synchronisation s'exécutent en arrière-plan sous le contrôle du système et appellent les méthodes ContentResolver
pour gérer les données.
Dans Android, le service Web avec lequel un adaptateur de synchronisation fonctionne est identifié par un type de compte. Chaque adaptateur de synchronisation fonctionne avec un type de compte, mais il peut accepter plusieurs noms de compte pour ce type. Les types et les noms de comptes sont décrits brièvement dans la section Sources de données de contacts brutes. Les définitions suivantes offrent plus de détails et décrivent le lien entre le type et le nom de compte, et les adaptateurs et services de synchronisation.
- Type de compte
- Identifie un service dans lequel l'utilisateur a stocké des données. La plupart du temps, l'utilisateur doit s'authentifier auprès du service. Par exemple, Google Contacts est un type de compte, identifié par le code
google.com
. Cette valeur correspond au type de compte utilisé parAccountManager
. - le nom de votre compte.
- Identifie un compte ou une connexion spécifique pour un type de compte. Les comptes Google Contacts sont identiques aux comptes Google, dont le nom correspond à une adresse e-mail. D'autres services peuvent utiliser un nom d'utilisateur ou un identifiant numérique en un mot.
Il n'est pas nécessaire que chaque type de compte soit unique. Un utilisateur peut configurer plusieurs comptes Google Contacts et télécharger ses données vers Contacts Provider. Cela peut se produire si l'utilisateur a un ensemble de contacts personnels pour un nom de compte personnel et un autre pour son compte professionnel. Les noms de compte sont généralement uniques. Ensemble, ils identifient un flux de données spécifique entre le fournisseur de contacts et un service externe.
Si vous souhaitez transférer les données de votre service vers Contacts Provider, vous devez écrire votre propre adaptateur de synchronisation. Pour en savoir plus, consultez la section Adaptateurs de synchronisation du fournisseur de contacts.
La figure 4 montre comment Contacts Provider s'intègre dans le flux de données sur les personnes. Dans la case "adaptateurs de synchronisation", chaque adaptateur est libellé en fonction de son type de compte.
![Flux de données sur les personnes](https://developer.android.google.cn/static/images/providers/ContactsDataFlow.png?authuser=19&hl=fr)
Figure 4. Flux de données du fournisseur de contacts
Autorisations requises
Les applications qui souhaitent accéder au fournisseur de contacts doivent demander les autorisations suivantes:
- Accès en lecture à une ou plusieurs tables
-
READ_CONTACTS
, spécifié dansAndroidManifest.xml
avec l'élément<uses-permission>
en tant que<uses-permission android:name="android.permission.READ_CONTACTS">
. - Accès en écriture à une ou plusieurs tables
-
WRITE_CONTACTS
, spécifié dansAndroidManifest.xml
avec l'élément<uses-permission>
en tant que<uses-permission android:name="android.permission.WRITE_CONTACTS">
.
Ces autorisations ne s'appliquent pas aux données du profil utilisateur. Le profil utilisateur et les autorisations requises sont abordés dans la section suivante, Profil utilisateur.
N'oubliez pas que les données de contact de l'utilisateur sont personnelles et sensibles. Les utilisateurs se soucient de la confidentialité de leurs données. Ils ne veulent donc pas que les applications collectent des données les concernant ou concernant leurs contacts. Si la raison pour laquelle vous avez besoin d'une autorisation pour accéder à ses contacts n'est pas évidente, celui-ci peut donner de mauvaises notes à votre application ou simplement refuser de l'installer.
Le profil utilisateur
La table ContactsContract.Contacts
comporte une seule ligne contenant les données de profil de l'utilisateur de l'appareil. Ces données décrivent le user
de l'appareil plutôt que l'un des contacts de l'utilisateur. La ligne de contacts de profil est associée à une ligne de contacts bruts pour chaque système qui utilise un profil.
Chaque ligne de contact brut du profil peut comporter plusieurs lignes de données. Les constantes permettant d'accéder au profil utilisateur sont disponibles dans la classe ContactsContract.Profile
.
L'accès au profil utilisateur nécessite des autorisations spéciales. En plus des autorisations READ_CONTACTS
et WRITE_CONTACTS
nécessaires pour la lecture et l'écriture, l'accès au profil utilisateur nécessite respectivement les autorisations android.Manifest.permission#READ_PROFILE et android.Manifest.permission#WRITE_PROFILE pour l'accès en lecture et en écriture.
N'oubliez pas que vous devez considérer le profil d'un utilisateur comme étant sensible. L'autorisation android.Manifest.permission#READ_PROFILE vous permet d'accéder aux données permettant d'identifier personnellement l'utilisateur de l'appareil. Veillez à indiquer à l'utilisateur pourquoi vous avez besoin des autorisations d'accès au profil utilisateur dans la description de votre application.
Pour récupérer la ligne de contact contenant le profil de l'utilisateur, appelez ContentResolver.query()
. Définissez l'URI de contenu sur CONTENT_URI
et ne fournissez aucun critère de sélection. Vous pouvez également utiliser cet URI de contenu comme URI de base pour récupérer les données ou contacts bruts du profil. Par exemple, cet extrait récupère les données du profil:
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);
Remarque:Si vous récupérez plusieurs lignes de contact et que vous souhaitez déterminer si l'une d'entre elles correspond au profil utilisateur, testez la colonne IS_USER_PROFILE
de la ligne. Cette colonne est définie sur "1" pour le profil utilisateur.
Métadonnées du fournisseur de contacts
Le fournisseur de contacts gère les données qui assurent le suivi de l'état des données de contact dans le dépôt. Ces métadonnées concernant le dépôt sont stockées dans différents emplacements, y compris dans les tables "Contacts bruts", "Données" et "Contacts", la table ContactsContract.Settings
et la table ContactsContract.SyncState
. Le tableau suivant montre l'effet de chacune de ces métadonnées:
Tableau 3 : Métadonnées dans le fournisseur de contacts
Tableau | Colonne | Valeurs | Signification |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
"0" : aucune modification depuis la dernière synchronisation. |
Marque les contacts bruts qui ont été modifiés sur l'appareil et qui doivent être synchronisés avec le serveur. Cette valeur est définie automatiquement par le fournisseur de contacts lorsque les applications Android mettent à jour une ligne.
Les adaptateurs de synchronisation qui modifient les contacts bruts ou les tables de données doivent toujours ajouter la chaîne |
"1" : modifié depuis la dernière synchronisation, doit être synchronisé avec le serveur. | |||
ContactsContract.RawContacts |
VERSION |
Numéro de version de cette ligne. | Le fournisseur de contacts incrémente automatiquement cette valeur chaque fois que la ligne ou les données associées changent. |
ContactsContract.Data |
DATA_VERSION |
Numéro de version de cette ligne. | Contacts Provider incrémente automatiquement cette valeur chaque fois que la ligne de données est modifiée. |
ContactsContract.RawContacts |
SOURCE_ID |
Valeur de chaîne qui identifie ce contact brut de manière unique dans le compte dans lequel il a été créé. |
Lorsqu'un adaptateur de synchronisation crée un contact brut, cette colonne doit être définie sur l'identifiant unique du serveur correspondant. Lorsqu'une application Android crée un contact brut, elle doit laisser cette colonne vide. Cela indique à l'adaptateur de synchronisation qu'il doit créer un contact brut sur le serveur et obtenir une valeur pour SOURCE_ID .
En particulier, l'ID source doit être unique pour chaque type de compte et doit être stable lors des synchronisations:
|
ContactsContract.Groups |
GROUP_VISIBLE |
"0" : les contacts de ce groupe ne doivent pas être visibles dans l'interface utilisateur des applications Android. | Cette colonne permet d'assurer la compatibilité avec les serveurs qui autorisent un utilisateur à masquer les contacts de certains groupes. |
"1" : les contacts de ce groupe peuvent être visibles dans l'interface utilisateur des applications. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
"0" : pour ce compte et ce type de compte, les contacts qui n'appartiennent à aucun groupe sont invisibles dans l'interface utilisateur des applications Android. |
Par défaut, les contacts sont invisibles si aucun de leurs contacts bruts ne fait partie d'un groupe (l'appartenance à un groupe d'un contact brut est indiquée par une ou plusieurs lignes ContactsContract.CommonDataKinds.GroupMembership dans la table ContactsContract.Data ).
En définissant cette option sur la ligne du tableau ContactsContract.Settings pour un type de compte et un compte, vous pouvez forcer la visibilité des contacts sans groupe.
Cette option permet notamment d'afficher les contacts de serveurs qui n'utilisent pas de groupes.
|
"1" : pour ce compte et ce type de compte, les contacts n'appartenant à aucun groupe sont visibles dans l'interface utilisateur des applications. | |||
ContactsContract.SyncState |
(tous) | Stockez les métadonnées de votre adaptateur de synchronisation à l'aide de ce tableau. | Cette table vous permet de stocker l'état de la synchronisation et d'autres données liées à la synchronisation de manière persistante sur l'appareil. |
Accès au fournisseur de contacts
Cette section décrit les consignes d'accès aux données du fournisseur de contacts et traite des points suivants:
- Requêtes d'entité.
- Modification par lot
- Récupération et modification avec des intents
- Intégrité des données.
La procédure à partir d'un adaptateur de synchronisation est également abordée plus en détail dans la section Adaptateurs de synchronisation du fournisseur de contacts.
Interroger des entités
Les tables du fournisseur de contacts étant organisées de façon hiérarchique, il est souvent utile de récupérer une ligne et toutes les lignes "enfants" qui y sont associées. Par exemple, pour afficher toutes les informations sur une personne, vous pouvez récupérer toutes les lignes ContactsContract.RawContacts
d'une seule ligne ContactsContract.Contacts
ou toutes les lignes ContactsContract.CommonDataKinds.Email
d'une seule ligne ContactsContract.RawContacts
. Pour faciliter ce processus, le fournisseur de contacts propose des constructions d'entités, qui agissent comme des jointures de base de données entre les tables.
Une entité est semblable à un tableau composé de colonnes sélectionnées à partir d'un tableau parent et de son tableau enfant.
Lorsque vous interrogez une entité, vous fournissez une projection et des critères de recherche basés sur les colonnes disponibles à partir de l'entité. Le résultat est un objet Cursor
contenant une ligne pour chaque ligne de la table enfant récupérée. Par exemple, si vous interrogez ContactsContract.Contacts.Entity
pour un nom de contact et toutes les lignes ContactsContract.CommonDataKinds.Email
pour tous les contacts bruts portant ce nom, vous obtenez un Cursor
contenant une ligne pour chaque ligne ContactsContract.CommonDataKinds.Email
.
Les entités simplifient les requêtes. À l'aide d'une entité, vous pouvez récupérer simultanément toutes les données de contact d'un contact ou d'un contact brut, ce qui vous évite d'avoir à interroger la table parent pour obtenir un ID, puis à interroger la table enfant avec cet ID. En outre, le fournisseur de contacts traite une requête sur une entité dans le cadre d'une transaction unique, ce qui garantit la cohérence interne des données récupérées.
Remarque:Une entité ne contient généralement pas toutes les colonnes des tables parentes et enfants. Si vous tentez d'utiliser un nom de colonne qui ne figure pas dans la liste des constantes de nom de colonne pour l'entité, vous obtiendrez une erreur Exception
.
L'extrait de code suivant montre comment récupérer toutes les lignes brutes d'un contact. L'extrait de code fait partie d'une application plus vaste comportant deux activités, "main" et "detail". L'activité principale affiche une liste de lignes de contacts. Lorsque l'utilisateur en sélectionne une, l'activité envoie son ID à l'activité détaillée. L'activité "Détail" utilise ContactsContract.Contacts.Entity
pour afficher toutes les lignes de données de tous les contacts bruts associés au contact sélectionné.
Cet extrait est issu de l'activité "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. }
Une fois le chargement terminé, LoaderManager
appelle un rappel à onLoadFinished()
. L'un des arguments entrants de cette méthode est un objet Cursor
contenant les résultats de la requête. Dans votre propre application, vous pouvez obtenir les données de ce Cursor
pour les afficher ou les utiliser davantage.
Modification par lot
Dans la mesure du possible, insérez, mettez à jour et supprimez des données dans le fournisseur de contacts en "mode de traitement par lot" en créant une ArrayList
d'objets ContentProviderOperation
et en appelant applyBatch()
. Étant donné que le fournisseur de contacts effectue toutes les opérations d'un élément applyBatch()
en une seule transaction, vos modifications ne laisseront jamais un état incohérent dans le dépôt de contacts. Une modification par lot facilite également l'insertion simultanée d'un contact brut et de ses données détaillées.
Remarque:Pour modifier un seul contact brut, envisagez d'envoyer un intent à l'application de contacts de l'appareil plutôt que de gérer la modification dans votre application. Pour en savoir plus, consultez la section Récupération et modification avec des intents.
Points de rendement
Une modification par lot contenant un grand nombre d'opérations peut bloquer d'autres processus, ce qui nuit à l'expérience utilisateur globale. Pour organiser toutes les modifications que vous souhaitez effectuer dans le moins de listes distinctes possible, tout en les empêchant de bloquer le système, vous devez définir des points de rendement pour une ou plusieurs opérations.
Un point de rendement est un objet ContentProviderOperation
dont la valeur isYieldAllowed()
est définie sur true
. Lorsque le fournisseur de contacts rencontre un point de rendement, il suspend son travail pour permettre l'exécution d'autres processus et ferme la transaction en cours. Lorsque le fournisseur redémarre, il passe à l'opération suivante dans ArrayList
et lance une nouvelle transaction.
Les points de rendement génèrent plusieurs transactions par appel à applyBatch()
. C'est pourquoi vous devez définir un point de rendement pour la dernière opération sur un ensemble de lignes associées.
Par exemple, vous devez définir un point de rendement pour la dernière opération d'un ensemble qui ajoute des lignes de contact brutes et les lignes de données associées, ou la dernière opération pour un ensemble de lignes associées à un seul contact.
Les points de rendement sont également une unité d'opération atomique. Tous les accès entre deux points de rendement aboutissent ou échouent comme une seule unité. Si vous ne définissez aucun point de rendement, la plus petite opération atomique est l'ensemble du lot d'opérations. Si vous utilisez des points de rendement, vous empêchez les opérations de dégrader les performances du système, tout en garantissant le caractère atomique d'un sous-ensemble d'opérations.
Modifier les anciennes références
Lorsque vous insérez une nouvelle ligne de contact brut et les lignes de données associées en tant qu'ensemble d'objets ContentProviderOperation
, vous devez associer les lignes de données à la ligne de contact brut en insérant la valeur _ID
du contact brut en tant que valeur RAW_CONTACT_ID
. Toutefois, cette valeur n'est pas disponible lorsque vous créez le ContentProviderOperation
pour la ligne de données, car vous n'avez pas encore appliqué le ContentProviderOperation
pour la ligne de contact brute. Pour contourner ce problème, la classe ContentProviderOperation.Builder
utilise la méthode withValueBackReference()
.
Cette méthode vous permet d'insérer ou de modifier une colonne avec le résultat d'une opération précédente.
La méthode withValueBackReference()
comporte deux arguments:
-
key
- Clé d'une paire clé-valeur. La valeur de cet argument doit correspondre au nom d'une colonne de la table que vous modifiez.
-
previousResult
- Index en base 0 d'une valeur dans le tableau d'objets
ContentProviderResult
deapplyBatch()
. À mesure que les opérations par lot sont appliquées, le résultat de chaque opération est stocké dans un tableau intermédiaire de résultats. La valeurpreviousResult
est l'index de l'un de ces résultats, qui est récupéré et stocké avec la valeurkey
. Cela vous permet d'insérer un nouvel enregistrement de contact brut et de récupérer sa valeur_ID
, puis de créer une "référence arrière" à la valeur lorsque vous ajoutez une ligneContactsContract.Data
.Le tableau de résultats complet est créé lorsque vous appelez
applyBatch()
pour la première fois, avec une taille égale à la taille duArrayList
des objetsContentProviderOperation
que vous fournissez. Cependant, tous les éléments du tableau de résultat sont définis surnull
. Si vous essayez de faire une référence arrière à un résultat pour une opération qui n'a pas encore été appliquée,withValueBackReference()
génère une erreurException
.
Les extraits de code suivants montrent comment insérer un nouveau contact brut et des données par lot. Elles incluent du code qui établit un point de rendement et utilise une référence arrière.
Le premier extrait récupère les données sur les contacts à partir de l'interface utilisateur. À ce stade, l'utilisateur a déjà sélectionné le compte pour lequel le nouveau contact brut doit être ajouté.
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());
L'extrait de code suivant crée une opération pour insérer la ligne de contact brute dans la table 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());
Ensuite, le code crée des lignes de données pour les lignes du nom à afficher, du numéro de téléphone et de l'adresse e-mail.
Chaque objet de compilateur d'opérations utilise withValueBackReference()
pour obtenir le RAW_CONTACT_ID
. La référence renvoie vers l'objet ContentProviderResult
de la première opération, qui ajoute la ligne de contact brute et renvoie sa nouvelle valeur _ID
. Par conséquent, chaque ligne de données est automatiquement associée par son RAW_CONTACT_ID
à la nouvelle ligne ContactsContract.RawContacts
à laquelle elle appartient.
L'objet ContentProviderOperation.Builder
qui ajoute la ligne d'adresse e-mail est signalé avec l'option withYieldAllowed()
, qui définit un point de rendement:
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());
Le dernier extrait montre l'appel à applyBatch()
qui insère les nouvelles lignes de données et de contacts brutes.
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); } }
Les opérations par lot vous permettent également de mettre en œuvre un contrôle de simultanéité optimiste, une méthode permettant d'appliquer des transactions de modification sans avoir à verrouiller le dépôt sous-jacent. Pour utiliser cette méthode, appliquez la transaction, puis recherchez d'autres modifications qui ont pu être apportées en même temps. Si vous constatez qu'une modification incohérente s'est produite, effectuez un rollback de la transaction, puis réessayez.
Le contrôle de simultanéité optimiste est utile pour un appareil mobile, lorsqu'il n'y a qu'un seul utilisateur à la fois et où les accès simultanés à un dépôt de données sont rares. Comme le verrouillage n'est pas utilisé, vous ne perdez pas de temps à définir des verrouillages ni à attendre que d'autres transactions le libèrent.
Pour utiliser le contrôle de simultanéité optimiste lors de la mise à jour d'une seule ligne ContactsContract.RawContacts
, procédez comme suit:
-
Récupérez la colonne
VERSION
du contact brut avec les autres données que vous récupérez. -
Créez un objet
ContentProviderOperation.Builder
adapté à l'application d'une contrainte à l'aide de la méthodenewAssertQuery(Uri)
. Pour l'URI de contenu, utilisezRawContacts.CONTENT_URI
en ajoutant l'_ID
du contact brut. -
Pour l'objet
ContentProviderOperation.Builder
, appelezwithValue()
pour comparer la colonneVERSION
au numéro de version que vous venez de récupérer. -
Pour le même
ContentProviderOperation.Builder
, appelezwithExpectedCount()
pour vous assurer qu'une seule ligne est testée par cette assertion. -
Appelez
build()
pour créer l'objetContentProviderOperation
, puis ajoutez-le en tant que premier objet duArrayList
que vous transmettez àapplyBatch()
. - Appliquez la transaction groupée.
Si la ligne de contact brut est mise à jour par une autre opération entre le moment où vous la lisez et celui où vous essayez de la modifier, l'instruction ContentProviderOperation
échoue, et l'ensemble du lot d'opérations est sauvegardé. Vous pouvez ensuite choisir de relancer le lot ou d'effectuer une autre action.
L'extrait de code suivant montre comment créer une ContentProviderOperation
"assertion" après avoir interrogé un seul contact brut à l'aide d'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 }
Récupération et modification avec des intents
L'envoi d'un intent à l'application Contacts de l'appareil vous permet d'accéder indirectement au fournisseur de contacts. L'intent lance l'interface utilisateur de l'application Contacts de l'appareil, dans laquelle les utilisateurs peuvent effectuer des tâches liées aux contacts. Avec ce type d'accès, les utilisateurs peuvent:
- Choisissez un contact dans une liste et renvoyez-le dans votre application pour y poursuivre le travail.
- Modifiez les données d'un contact existant.
- Insérez un nouveau contact brut pour l'un de ses comptes.
- Supprimer un contact ou les données de contacts
Si l'utilisateur insère ou met à jour des données, vous pouvez d'abord les collecter et les envoyer avec l'intent.
Lorsque vous utilisez des intents pour accéder au fournisseur de contacts via l'application Contacts de l'appareil, vous n'avez pas besoin d'écrire votre propre interface utilisateur ni votre propre code pour accéder au fournisseur. Vous n'avez pas non plus besoin de demander une autorisation de lecture ou d'écriture sur le fournisseur. L'application Contacts de l'appareil peut vous déléguer l'autorisation de lecture d'un contact. Étant donné que vous apportez des modifications au fournisseur via une autre application, vous n'avez pas besoin de disposer d'autorisations en écriture.
Le processus général d'envoi d'un intent pour accéder à un fournisseur est décrit en détail dans le guide
Principes de base du fournisseur de contenu de la section "Accès aux données via des intents". L'action, le type MIME et les valeurs de données que vous utilisez pour les tâches disponibles sont résumés dans le tableau 4, tandis que les valeurs supplémentaires que vous pouvez utiliser avec putExtra()
sont listées dans la documentation de référence pour ContactsContract.Intents.Insert
:
Tableau 4. Contacte les intents du fournisseur
Tâche | Action | Données | Type MIME | Notes |
---|---|---|---|---|
Sélectionner un contact dans une liste | ACTION_PICK |
Choisissez l'une des options suivantes :
|
Non utilisé |
Affiche une liste de contacts bruts ou une liste de données provenant d'un contact brut, en fonction du type d'URI de contenu que vous fournissez.
Appelez |
Insérer un nouveau contact brut | Insert.ACTION |
N/A |
RawContacts.CONTENT_TYPE , type MIME pour un ensemble de contacts bruts.
|
Affiche l'écran Ajouter un contact de l'application Contacts de l'appareil. Les valeurs d'extras que vous ajoutez à l'intent s'affichent. S'il est envoyé avec startActivityForResult() , l'URI de contenu du contact brut nouvellement ajouté est renvoyé à la méthode de rappel onActivityResult() de votre activité dans l'argument Intent , dans le champ "data". Pour obtenir cette valeur, appelez getData() .
|
Modifier un contact | ACTION_EDIT |
CONTENT_LOOKUP_URI pour le contact. L'activité d'édition permettra à l'utilisateur de modifier toutes les données associées à ce contact.
|
Contacts.CONTENT_ITEM_TYPE , un seul contact. |
Affiche l'écran Modifier le contact dans l'application Contacts. Les valeurs supplémentaires que vous ajoutez à l'intent s'affichent. Lorsque l'utilisateur clique sur OK pour enregistrer les modifications, votre activité revient au premier plan. |
Affichez un sélecteur qui peut également ajouter des données. | ACTION_INSERT_OR_EDIT |
N/A |
CONTENT_ITEM_TYPE
|
Cet intent affiche toujours l'écran de sélection de l'application Contacts. L'utilisateur peut sélectionner un contact à modifier ou en ajouter un. L'écran de modification ou d'ajout apparaît, selon le choix de l'utilisateur, et les données supplémentaires que vous transmettez à l'intent s'affichent. Si votre application affiche des données de contact telles qu'une adresse e-mail ou un numéro de téléphone, utilisez cet intent pour permettre à l'utilisateur d'ajouter ces données à un contact existant.
de contact,
Remarque:Il n'est pas nécessaire d'envoyer une valeur de nom dans les extras de cet intent, car l'utilisateur choisit toujours un nom existant ou en ajoute un nouveau. De plus, si vous envoyez un nom et que l'utilisateur choisit d'effectuer une modification, l'application Contacts affiche le nom que vous envoyez en remplaçant la valeur précédente. Si l'utilisateur ne s'en aperçoit pas et enregistre la modification, l'ancienne valeur est perdue. |
L'application Contacts de l'appareil ne vous permet pas de supprimer un contact brut ni aucune de ses données avec un intent. Pour supprimer un contact brut, utilisez plutôt ContentResolver.delete()
ou ContentProviderOperation.newDelete()
.
L'extrait de code suivant montre comment construire et envoyer un intent qui insère un nouveau contact et des données brutes:
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);
Intégrité des données
Étant donné que le dépôt de contacts contient des données importantes et sensibles que les utilisateurs s'attendent à trouver correctes et à jour, le fournisseur de contacts dispose de règles bien définies pour l'intégrité des données. Il est de votre responsabilité de vous conformer à ces règles lorsque vous modifiez les données des contacts. Les règles importantes sont listées ici:
-
Ajoutez toujours une ligne
ContactsContract.CommonDataKinds.StructuredName
pour chaque ligneContactsContract.RawContacts
que vous ajoutez. - Une ligne
ContactsContract.RawContacts
sans ligneContactsContract.CommonDataKinds.StructuredName
dans la tableContactsContract.Data
peut entraîner des problèmes lors de l'agrégation. -
Associez toujours les nouvelles lignes
ContactsContract.Data
à leur ligneContactsContract.RawContacts
parente. -
Une ligne
ContactsContract.Data
qui n'est pas associée à unContactsContract.RawContacts
n'est pas visible dans l'application Contacts de l'appareil, et peut causer des problèmes avec les adaptateurs de synchronisation. - Ne modifiez que les données de contacts bruts qui vous appartiennent.
- N'oubliez pas que le fournisseur de contacts gère généralement les données de différents types de comptes et services en ligne. Vous devez vous assurer que votre application ne modifie ou ne supprime que les données des lignes qui vous appartiennent, et qu'elle n'insère que les données dont le type et le nom de compte sont que vous contrôlez.
-
Utilisez toujours les constantes définies dans
ContactsContract
et ses sous-classes pour les autorités, les URI de contenu, les chemins d'URI, les noms de colonne, les types MIME et les valeursTYPE
. - L'utilisation de ces constantes vous permet d'éviter les erreurs. Vous recevrez également des avertissements du compilateur si l'une des constantes est obsolète.
Lignes de données personnalisées
En créant et en utilisant vos propres types MIME personnalisés, vous pouvez insérer, modifier, supprimer et récupérer vos propres lignes de données dans la table ContactsContract.Data
. Vos lignes sont limitées à l'utilisation de la colonne définie dans ContactsContract.DataColumns
, bien que vous puissiez mapper vos propres noms de colonnes spécifiques au type avec les noms de colonne par défaut. Dans l'application Contacts de l'appareil, les données de vos lignes sont affichées, mais ne peuvent pas être modifiées ni supprimées, et les utilisateurs ne peuvent pas ajouter de données supplémentaires. Pour permettre aux utilisateurs de modifier vos lignes de données personnalisées, vous devez fournir une activité d'édition dans votre propre application.
Pour afficher vos données personnalisées, fournissez un fichier contacts.xml
contenant un élément <ContactsAccountType>
et un ou plusieurs de ses éléments enfants <ContactsDataKind>
. Pour en savoir plus, consultez la section <ContactsDataKind> element
.
Pour en savoir plus sur les types MIME personnalisés, consultez le guide Créer un fournisseur de contenu.
Adaptateurs de synchronisation du fournisseur de contacts
Le fournisseur de contacts est spécialement conçu pour gérer la synchronisation des données de contacts entre un appareil et un service en ligne. Les utilisateurs peuvent ainsi télécharger des données existantes sur un nouvel appareil et les importer dans un nouveau compte. La synchronisation garantit également que les utilisateurs disposent des données les plus récentes, quelle que soit la source des ajouts et des modifications. La synchronisation présente un autre avantage : les données des contacts sont disponibles même lorsque l'appareil n'est pas connecté au réseau.
Bien que vous puissiez implémenter la synchronisation de différentes manières, le système Android fournit un framework de synchronisation de plug-ins qui automatise les tâches suivantes:
- Vérification de la disponibilité du réseau...
- Planification et exécution de la synchronisation en fonction des préférences de l'utilisateur
- Redémarrage des synchronisations arrêtées.
Pour utiliser ce framework, vous devez fournir un plug-in d'adaptateur de synchronisation. Chaque adaptateur de synchronisation est propre à un fournisseur de services et de contenu, mais peut gérer plusieurs noms de compte pour le même service. Le framework permet également d'utiliser plusieurs adaptateurs de synchronisation pour le même service et le même fournisseur.
Classes et fichiers d'adaptateur de synchronisation
Vous allez implémenter un adaptateur de synchronisation en tant que sous-classe de AbstractThreadedSyncAdapter
et l'installer dans le cadre d'une application Android. Le système apprend à reconnaître l'adaptateur de synchronisation à partir des éléments du fichier manifeste de votre application et d'un fichier XML spécial vers lequel pointe le fichier manifeste. Le fichier XML définit le type de compte du service en ligne et l'autorité du fournisseur de contenu, qui identifient ensemble l'adaptateur de manière unique. L'adaptateur de synchronisation ne devient actif que lorsque l'utilisateur ajoute un compte pour son type et active la synchronisation pour le fournisseur de contenu avec lequel l'adaptateur de synchronisation se synchronise. À ce stade, le système commence à gérer l'adaptateur, en l'appelant si nécessaire pour la synchronisation entre le fournisseur de contenu et le serveur.
Remarque:L'utilisation d'un type de compte dans l'identification de l'adaptateur de synchronisation permet au système de détecter et de regrouper les adaptateurs de synchronisation qui accèdent à différents services de la même organisation. Par exemple, les adaptateurs de synchronisation pour les services en ligne Google sont tous du même type de compte com.google
. Lorsque les utilisateurs ajoutent un compte Google à leurs appareils, tous les adaptateurs de synchronisation pour les services Google installés sont listés ensemble, et chacun d'eux se synchronise avec un fournisseur de contenu différent sur l'appareil.
Étant donné que la plupart des services nécessitent que les utilisateurs valident leur identité avant d'accéder aux données, le système Android propose un framework d'authentification semblable au framework de l'adaptateur de synchronisation, qui est souvent utilisé conjointement. Le framework d'authentification utilise des authentificateurs de plug-ins qui sont des sous-classes de AbstractAccountAuthenticator
. Un authentificateur vérifie l'identité de l'utilisateur en procédant comme suit:
- Collecte le nom, le mot de passe ou des informations similaires de l'utilisateur (ses identifiants).
- Il envoie les identifiants au service.
- Examinez la réponse du service.
Si le service les accepte, l'authentificateur peut les stocker pour une utilisation ultérieure. En raison du framework d'authentification de plug-in, AccountManager
peut fournir l'accès à tous les jetons d'authentification compatibles avec un authentificateur et qu'il choisit d'exposer, tels que les jetons d'authentification OAuth2.
Bien que l'authentification ne soit pas requise, la plupart des services de gestion des contacts l'utilisent. Toutefois, vous n'êtes pas obligé d'utiliser le framework d'authentification Android pour effectuer l'authentification.
Implémentation de l'adaptateur de synchronisation
Pour implémenter un adaptateur de synchronisation pour le fournisseur de contacts, commencez par créer une application Android contenant les éléments suivants:
-
Un composant
Service
qui répond aux requêtes du système de liaison à l'adaptateur de synchronisation. - Lorsque le système souhaite exécuter une synchronisation, il appelle la méthode
onBind()
du service afin d'obtenir unIBinder
pour l'adaptateur de synchronisation. Cela permet au système d'effectuer des appels inter-processus aux méthodes de l'adaptateur. -
L'adaptateur de synchronisation réel, implémenté en tant que sous-classe concrète de
AbstractThreadedSyncAdapter
. - Cette classe permet de télécharger des données à partir du serveur, d'importer des données depuis l'appareil et de résoudre les conflits. Le travail principal de l'adaptateur est effectué dans la méthode
onPerformSync()
. Cette classe doit être instanciée en tant que singleton. -
Une sous-classe de
Application
. -
Cette classe sert de fabrique pour le singleton de l'adaptateur de synchronisation. Utilisez la méthode
onCreate()
pour instancier l'adaptateur de synchronisation et fournissez une méthode "getter" statique pour renvoyer le singleton à la méthodeonBind()
du service de l'adaptateur de synchronisation. -
Facultatif:composant
Service
qui répond aux requêtes du système d'authentification des utilisateurs. -
AccountManager
démarre ce service pour commencer le processus d'authentification. La méthodeonCreate()
du service instancie un objet authentificateur. Lorsque le système souhaite authentifier un compte utilisateur pour l'adaptateur de synchronisation de l'application, il appelle la méthodeonBind()
du service afin d'obtenir unIBinder
pour l'authentificateur. Cela permet au système d'effectuer des appels inter-processus aux méthodes de l'authentificateur. -
Facultatif:sous-classe concrète de
AbstractAccountAuthenticator
qui gère les requêtes d'authentification. - Cette classe fournit des méthodes que
AccountManager
appelle pour authentifier les identifiants de l'utilisateur auprès du serveur. Les détails du processus d'authentification varient considérablement selon la technologie de serveur utilisée. Reportez-vous à la documentation de votre logiciel serveur pour en savoir plus sur l'authentification. - Fichiers XML qui définissent l'adaptateur de synchronisation et l'authentificateur au système.
- Les composants de l'adaptateur de synchronisation et du service d'authentification décrits précédemment sont définis dans les éléments
<service>
du fichier manifeste de l'application. Ces éléments contiennent des éléments enfants<meta-data>
qui fournissent des données spécifiques au système :-
L'élément
<meta-data>
pour le service de l'adaptateur de synchronisation pointe vers le fichier XMLres/xml/syncadapter.xml
. Ce fichier spécifie un URI pour le service Web qui sera synchronisé avec le fournisseur de contacts et un type de compte pour le service Web. -
Facultatif:L'élément
<meta-data>
de l'authentificateur pointe vers le fichier XMLres/xml/authenticator.xml
. Ce fichier spécifie ensuite le type de compte pris en charge par cet authentificateur, ainsi que les ressources d'interface utilisateur qui apparaissent lors du processus d'authentification. Le type de compte spécifié dans cet élément doit être identique à celui spécifié pour l'adaptateur de synchronisation.
-
L'élément
Données des flux de réseaux sociaux
Les tables android.provider.ContactsContract.StreamItems et android.provider.ContactsContract.StreamItemPhotos gèrent les données entrantes des réseaux sociaux. Vous pouvez écrire un adaptateur de synchronisation qui ajoute des données de flux de votre propre réseau à ces tables. Vous pouvez également lire les données de flux de ces tables et les afficher dans votre propre application, ou les deux. Grâce à ces fonctionnalités, vos services et applications de réseau social peuvent être intégrés à l'expérience de réseau social d'Android.
Texte du flux sur les réseaux sociaux
Les éléments de flux sont toujours associés à un contact brut. android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID renvoie à la valeur _ID
du contact brut. Le type et le nom du compte du contact brut sont également stockés dans la ligne de l'élément de flux.
Stockez les données de votre flux dans les colonnes suivantes:
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE
- Obligatoire. Type de compte de l'utilisateur pour le contact brut associé à cet élément de flux. N'oubliez pas de définir cette valeur lorsque vous insérez un élément de flux.
- android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME
- Obligatoire. Nom du compte utilisateur pour le contact brut associé à cet élément de flux. N'oubliez pas de définir cette valeur lorsque vous insérez un élément de flux.
- Colonnes d'identifiant
-
Obligatoire. Lorsque vous insérez un élément de flux, vous devez insérer les colonnes d'identifiants suivantes :
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_ID: valeur android.provider.BaseColumns#_ID du contact auquel cet élément de flux est associé.
- android.provider.ContactsContract.StreamItemsColumns#CONTACT_LOOKUP_KEY: valeur android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY du contact auquel cet élément de flux est associé.
- android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID: valeur android.provider.BaseColumns#_ID du contact brut auquel cet élément de flux est associé.
- android.provider.ContactsContract.StreamItemsColumns#COMMENTS
- Facultatif. Stocke les informations récapitulatives que vous pouvez afficher au début d'un élément de flux.
- android.provider.ContactsContract.StreamItemsColumns#TEXT
-
Texte de l'élément de flux (contenu publié par sa source ou description d'une action ayant généré l'élément de flux). Cette colonne peut contenir n'importe quelle mise en forme et images de ressources intégrées pouvant être affichées par
fromHtml()
. Le fournisseur peut tronquer le contenu long ou définir des points de suspension, mais il s'efforcera d'éviter de casser les balises. - android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
- Chaîne de texte contenant l'heure à laquelle l'élément de flux a été inséré ou mis à jour, exprimée en millisecondes depuis l'epoch. Les applications qui insèrent ou mettent à jour des éléments de flux sont responsables de la maintenance de cette colonne. Cette colonne n'est pas automatiquement gérée par le fournisseur de contacts.
Pour afficher les informations d'identification de vos éléments de flux, utilisez android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL et android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE pour un lien vers les ressources de votre application.
La table android.provider.ContactsContract.StreamItems contient également les colonnes android.provider.ContactsContract.StreamItemsColumns#SYNC1 via android.provider.ContactsContract.StreamItemsColumns#SYNC4, pour l'utilisation exclusive des adaptateurs de synchronisation.
Photos des flux de réseaux sociaux
La table android.provider.ContactsContract.StreamItemPhotos stocke les photos associées à un élément de flux. La colonne android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID de la table renvoie aux valeurs de la colonne _ID
de la table android.provider.ContactsContract.StreamItems. Les références photo sont stockées dans le tableau dans les colonnes suivantes:
- Colonne android.provider.ContactsContract.StreamItemPhotos#PHOTO (BLOB).
- Représentation binaire de la photo, redimensionnée par le fournisseur à des fins de stockage et d'affichage. Cette colonne est disponible pour assurer la rétrocompatibilité avec les versions précédentes du fournisseur de contacts qui l'utilisaient pour stocker des photos. Toutefois, dans la version actuelle, vous ne devez pas utiliser cette colonne pour stocker des photos. Utilisez plutôt android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID ou android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI (ces deux méthodes sont décrites ci-dessous) pour stocker des photos dans un fichier. Cette colonne contient maintenant une vignette de la photo, qui peut être consultée.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
Identifiant numérique d'une photo pour un contact brut. Ajoutez cette valeur à la constante
DisplayPhoto.CONTENT_URI
pour obtenir un URI de contenu pointant vers un seul fichier photo, puis appelezopenAssetFileDescriptor()
pour obtenir un handle vers le fichier photo. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
URI de contenu pointant directement vers le fichier photo de la photo représentée par cette ligne.
Appelez
openAssetFileDescriptor()
avec cet URI pour obtenir un handle vers le fichier photo.
Utiliser les tableaux de flux de réseaux sociaux
Ces tableaux fonctionnent de la même manière que les autres tableaux principaux du fournisseur de contacts, à une différence près:
- Ces tables nécessitent des autorisations d'accès supplémentaires. Pour pouvoir les lire, votre application doit disposer de l'autorisation android.Manifest.permission#READ_SOCIAL_STREAM. Pour les modifier, votre application doit disposer de l'autorisation android.Manifest.permission#WRITE_SOCIAL_STREAM.
-
Pour la table android.provider.ContactsContract.StreamItems, le nombre de lignes stockées pour chaque contact brut est limité. Une fois cette limite atteinte, le fournisseur de contacts fait de la place pour les nouvelles lignes d'éléments de flux en supprimant automatiquement les lignes contenant le plus ancien android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP. Pour connaître la limite, envoyez une requête à l'URI de contenu android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI. Vous pouvez laisser tous les arguments autres que l'URI de contenu définis sur
null
. La requête renvoie un curseur contenant une seule ligne, avec la seule colonne android.provider.ContactsContract.StreamItems#MAX_ITEMS.
La classe android.provider.ContactsContract.StreamItems.StreamItemPhotos définit un sous-tableau d'android.provider.ContactsContract.StreamItemPhotos contenant les lignes des photos d'un seul élément de flux.
Interactions avec les flux sur les réseaux sociaux
Les données de flux sur les réseaux sociaux gérées par le fournisseur de contacts conjointement avec l'application de contacts de l'appareil constituent un moyen efficace de connecter votre système de réseau social à vos contacts existants. Les fonctionnalités suivantes sont disponibles:
- En synchronisant votre service de réseau social avec le fournisseur de contacts à l'aide d'un adaptateur de synchronisation, vous pouvez récupérer l'activité récente des contacts d'un utilisateur et la stocker dans les tables android.provider.ContactsContract.StreamItems et android.provider.ContactsContract.StreamItemPhotos pour une utilisation ultérieure.
- En plus d'effectuer une synchronisation régulière, vous pouvez déclencher votre adaptateur de synchronisation pour récupérer des données supplémentaires lorsque l'utilisateur sélectionne un contact à afficher. Cela permet à votre adaptateur de synchronisation de récupérer des photos haute résolution et les derniers éléments de flux du contact.
- En enregistrant une notification dans l'application Contacts de l'appareil et dans le fournisseur de contacts, vous pouvez recevoir un intent lorsqu'un contact est consulté, et mettre à jour l'état du contact depuis votre service. Cette approche peut s'avérer plus rapide et utiliser moins de bande passante qu'une synchronisation complète avec un adaptateur de synchronisation.
- Les utilisateurs peuvent ajouter un contact à votre service de réseau social tout en le consultant dans l'application Contacts de l'appareil. Pour ce faire, vous pouvez utiliser la fonctionnalité "Inviter un contact", à laquelle vous associez une activité qui ajoute un contact existant à votre réseau, et un fichier XML qui fournit les détails de votre application à l'application de gestion des contacts de l'appareil et au fournisseur de contacts.
La synchronisation régulière des éléments de flux avec Contacts Provider est la même que pour les autres synchronisations. Pour en savoir plus sur la synchronisation, consultez la section Adaptateurs de synchronisation du fournisseur de contacts. L'enregistrement de notifications et l'invitation de contacts sont abordés dans les deux sections suivantes.
S'inscrire pour gérer les vues liées aux réseaux sociaux
Pour enregistrer votre adaptateur de synchronisation afin de recevoir des notifications lorsque l'utilisateur affiche un contact géré par votre adaptateur de synchronisation, procédez comme suit:
-
Créez un fichier nommé
contacts.xml
dans le répertoireres/xml/
de votre projet. Si vous disposez déjà de ce fichier, vous pouvez ignorer cette étape. -
Dans ce fichier, ajoutez l'élément
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Si cet élément existe déjà, vous pouvez ignorer cette étape. -
Pour enregistrer un service qui est averti lorsque l'utilisateur ouvre la page d'informations d'un contact dans l'application Contacts de l'appareil, ajoutez l'attribut
viewContactNotifyService="serviceclass"
à l'élément, oùserviceclass
est le nom de classe complet du service qui doit recevoir l'intent de l'application Contacts de l'appareil. Pour le service d'alerte, utilisez une classe qui étendIntentService
afin de permettre au service de recevoir des intents. Les données de l'intent entrant contiennent l'URI de contenu du contact brut sur lequel l'utilisateur a cliqué. À partir du service d'alerte, vous pouvez associer votre adaptateur de synchronisation, puis l'appeler pour mettre à jour les données du contact brut.
Pour enregistrer une activité à appeler lorsque l'utilisateur clique sur un élément du flux, une photo ou les deux:
-
Créez un fichier nommé
contacts.xml
dans le répertoireres/xml/
de votre projet. Si vous disposez déjà de ce fichier, vous pouvez ignorer cette étape. -
Dans ce fichier, ajoutez l'élément
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Si cet élément existe déjà, vous pouvez ignorer cette étape. -
Pour enregistrer l'une de vos activités afin de gérer les clics des utilisateurs sur un élément de flux dans l'application Contacts de l'appareil, ajoutez l'attribut
viewStreamItemActivity="activityclass"
à l'élément, oùactivityclass
est le nom de classe complet de l'activité qui doit recevoir l'intent de l'application Contacts de l'appareil. -
Pour enregistrer l'une de vos activités afin de gérer le clic d'un utilisateur sur une photo de flux dans l'application Contacts de l'appareil, ajoutez l'attribut
viewStreamItemPhotoActivity="activityclass"
à l'élément, oùactivityclass
est le nom de classe complet de l'activité qui doit recevoir l'intent de l'application Contacts de l'appareil.
L'élément <ContactsAccountType>
est décrit plus en détail dans la section Élément <ContactsAccountType>.
L'intent entrant contient l'URI de contenu de l'élément ou de la photo sur lequel l'utilisateur a cliqué. Pour avoir des activités distinctes pour les éléments textuels et les photos, utilisez les deux attributs dans le même fichier.
Interagir avec votre service de réseau social
Les utilisateurs n'ont pas besoin de quitter l'application de contacts de l'appareil pour inviter un contact sur votre site de réseau social. À la place, vous pouvez demander à l'application de contacts de l'appareil d'envoyer un intent pour inviter le contact à l'une de vos activités. Pour configurer une expérience supervisée :
-
Créez un fichier nommé
contacts.xml
dans le répertoireres/xml/
de votre projet. Si vous disposez déjà de ce fichier, vous pouvez ignorer cette étape. -
Dans ce fichier, ajoutez l'élément
<ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android">
. Si cet élément existe déjà, vous pouvez ignorer cette étape. -
Ajoutez les attributs suivants :
inviteContactActivity="activityclass"
-
inviteContactActionLabel="@string/invite_action_label"
activityclass
est le nom de classe complet de l'activité qui doit recevoir l'intent. La valeurinvite_action_label
est une chaîne de texte qui s'affiche dans le menu Ajouter une connexion de l'application Contacts de l'appareil.
Remarque:ContactsSource
est un nom de balise obsolète pour ContactsAccountType
.
Documentation de référence sur contacts.xml
Le fichier contacts.xml
contient des éléments XML qui contrôlent l'interaction de votre adaptateur de synchronisation et de votre application avec l'application Contacts et le fournisseur de contacts. Ces éléments sont décrits dans les sections suivantes.
Élément <ContactsAccountType>
L'élément <ContactsAccountType>
contrôle l'interaction de votre application avec l'application Contacts. Sa syntaxe est la suivante:
<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">
Contenu:
res/xml/contacts.xml
peut contenir:
<ContactsDataKind>
Description :
Déclare les composants Android et les libellés d'interface utilisateur permettant aux utilisateurs d'inviter l'un de leurs contacts à rejoindre un réseau social, d'informer les utilisateurs lorsque l'un de leurs flux de réseau social est mis à jour, etc.
Notez que le préfixe d'attribut android:
n'est pas nécessaire pour les attributs de <ContactsAccountType>
.
Attributs:
inviteContactActivity
- Nom de classe complet de l'activité de votre application que vous souhaitez activer lorsque l'utilisateur sélectionne Ajouter une connexion dans l'application Contacts de l'appareil.
inviteContactActionLabel
- Chaîne de texte affichée pour l'activité spécifiée dans
inviteContactActivity
, dans le menu Ajouter une connexion. Par exemple, vous pouvez utiliser la chaîne "Suivre dans mon réseau". Vous pouvez utiliser un identifiant de ressource de chaîne pour ce libellé. viewContactNotifyService
- Nom de classe complet d'un service de votre application devant recevoir des notifications lorsque l'utilisateur affiche un contact. Cette notification est envoyée par l'application Contacts de l'appareil. Elle permet à votre application de reporter les opérations qui consomment beaucoup de données jusqu'à ce qu'elles soient nécessaires. Par exemple, votre application peut répondre à cette notification en lisant et en affichant la photo haute résolution du contact et les éléments de flux de réseaux sociaux les plus récents. Pour en savoir plus sur cette fonctionnalité, consultez la section Interactions avec des flux sur les réseaux sociaux.
viewGroupActivity
- Nom de classe complet d'une activité de votre application pouvant afficher des informations de groupe. Lorsque l'utilisateur clique sur le libellé de groupe dans l'application Contacts de l'appareil, l'interface utilisateur de cette activité s'affiche.
viewGroupActionLabel
- Libellé affiché par l'application Contacts pour une commande d'interface utilisateur qui permet à l'utilisateur d'afficher des groupes dans votre application.
Un identifiant de ressource de chaîne est autorisé pour cet attribut.
viewStreamItemActivity
- Nom de classe complet d'une activité de votre application que l'application de contacts de l'appareil lance lorsque l'utilisateur clique sur un élément de flux pour rechercher un contact brut.
viewStreamItemPhotoActivity
- Nom de classe complet d'une activité de votre application que l'application de contacts de l'appareil lance lorsque l'utilisateur clique sur une photo de l'élément du flux pour rechercher un contact brut.
Élément <ContactsDataKind>
L'élément <ContactsDataKind>
contrôle l'affichage des lignes de données personnalisées de votre application dans l'interface utilisateur de l'application Contacts. Sa syntaxe est la suivante:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
Contenu:
<ContactsAccountType>
Description :
Utilisez cet élément pour que l'application Contacts affiche le contenu d'une ligne de données personnalisée dans les détails d'un contact brut. Chaque élément enfant <ContactsDataKind>
de <ContactsAccountType>
représente un type de ligne de données personnalisée que votre adaptateur de synchronisation ajoute à la table ContactsContract.Data
. Ajoutez un élément <ContactsDataKind>
pour chaque type MIME personnalisé que vous utilisez. Vous n'avez pas besoin d'ajouter l'élément si vous disposez d'une ligne de données personnalisée pour laquelle vous ne souhaitez pas afficher de données.
Attributs:
android:mimeType
- Type MIME personnalisé que vous avez défini pour l'un de vos types de lignes de données personnalisées dans la table
ContactsContract.Data
. Par exemple, la valeurvnd.android.cursor.item/vnd.example.locationstatus
peut être un type MIME personnalisé pour une ligne de données qui enregistre la dernière position connue d'un contact. android:icon
- Ressource drawable Android que l'application Contacts affiche à côté de vos données. Utilisez-le pour indiquer à l'utilisateur que les données proviennent de votre service.
android:summaryColumn
- Nom de colonne pour la première des deux valeurs récupérées à partir de la ligne de données. La valeur s'affiche sur la première ligne de l'entrée pour cette ligne de données. La première ligne est destinée à être utilisée comme un résumé des données, mais cela est facultatif. Voir aussi android:detailColumn.
android:detailColumn
-
Nom de colonne pour la seconde des deux valeurs récupérées à partir de la ligne de données. La valeur s'affiche dans la deuxième ligne de l'entrée pour cette ligne de données. Voir aussi
android:summaryColumn
.
Fonctionnalités supplémentaires du fournisseur de contacts
Outre les principales fonctionnalités décrites dans les sections précédentes, le fournisseur de contacts propose les fonctionnalités utiles suivantes pour utiliser les données des contacts:
- Groupes de contacts
- Fonctionnalités photo
Groupes de contacts
Le fournisseur de contacts peut éventuellement ajouter des libellés à des collections de contacts associés à l'aide de données de groupe. Si le serveur associé à un compte utilisateur souhaite gérer des groupes, l'adaptateur de synchronisation pour le type de compte du compte doit transférer les données des groupes entre le fournisseur de contacts et le serveur. Lorsque les utilisateurs ajoutent un nouveau contact au serveur, puis le placent dans un nouveau groupe, l'adaptateur de synchronisation doit ajouter le nouveau groupe à la table ContactsContract.Groups
. Le ou les groupes auxquels appartient un contact brut sont stockés dans la table ContactsContract.Data
à l'aide du type MIME ContactsContract.CommonDataKinds.GroupMembership
.
Si vous concevez un adaptateur de synchronisation qui ajoute des données de contact brutes du serveur au fournisseur de contacts et que vous n'utilisez pas de groupes, vous devez demander au fournisseur de rendre vos données visibles. Dans le code exécuté lorsqu'un utilisateur ajoute un compte à l'appareil, mettez à jour la ligne ContactsContract.Settings
ajoutée par le fournisseur de contacts pour le compte. Sur cette ligne, définissez la valeur de la colonne Settings.UNGROUPED_VISIBLE
sur 1. Dans ce cas, Contacts Provider rend toujours les données de vos contacts visibles, même si vous n'utilisez pas de groupes.
Photos du contact
La table ContactsContract.Data
stocke les photos sous forme de lignes de type MIME Photo.CONTENT_ITEM_TYPE
. La colonne CONTACT_ID
de la ligne est associée à la colonne _ID
du contact brut auquel elle appartient.
La classe ContactsContract.Contacts.Photo
définit un sous-tableau de ContactsContract.Contacts
contenant des informations sur la photo principale d'un contact, qui est la photo principale du contact brut principal. De même, la classe ContactsContract.RawContacts.DisplayPhoto
définit un sous-tableau de ContactsContract.RawContacts
contenant des informations sur la photo principale d'un contact brut.
La documentation de référence pour ContactsContract.Contacts.Photo
et ContactsContract.RawContacts.DisplayPhoto
contient des exemples de récupération d'informations sur les photos. Il n'existe pas de classe pratique permettant de récupérer la vignette principale d'un contact brut, mais vous pouvez envoyer une requête à la table ContactsContract.Data
, en sélectionnant l'_ID
du contact brut, la Photo.CONTENT_ITEM_TYPE
et la colonne IS_PRIMARY
pour trouver la ligne de la photo principale du contact brut.
Les données de flux sur les réseaux sociaux d'une personne peuvent également inclure des photos. Celles-ci sont stockées dans la table android.provider.ContactsContract.StreamItemPhotos, plus en détail dans la section Photos de flux de réseaux sociaux.