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 affichées dans l'application Contacts de l'appareil. Vous pouvez également accéder à ses données dans votre propre application et transférer des données entre l'appareil et les services en ligne. Le fournisseur accepte un large éventail de sources de données et tente de gérer autant de données que possible pour chaque personne. Son organisation est donc complexe. C'est pourquoi l'API du fournisseur inclut un vaste ensemble de classes de contrat et d'interfaces qui facilitent à la fois 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 avec le fournisseur de contacts
Ce guide part 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
Le fournisseur de contacts est un composant de fournisseur de contenu Android. Il gère trois types de données sur une personne, chacun correspondant à une table proposée par le fournisseur, comme illustré dans la figure 1:
Les trois tables sont communément appelées par les noms de leurs classes contractuelles. Les classes définissent des constantes pour les URI de contenu, les noms de colonnes et les valeurs de colonne utilisées par les tables:
-
ContactsContract.Contacts
tableau - Lignes représentant différentes personnes, en fonction de l'agrégation des lignes de contacts brutes.
-
ContactsContract.RawContacts
table - Lignes contenant un résumé des données d'une personne, spécifiques à un type et à un compte utilisateur.
-
ContactsContract.Data
table - Lignes contenant les informations du contact brut, telles 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 que le fournisseur de contacts utilise 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 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.
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 sa ligne ContactsContract.RawContacts
parente.
Colonnes importantes des contacts bruts
Les colonnes importantes de la table ContactsContract.RawContacts
sont listées dans le tableau 1. Veuillez lire les notes qui suivent le tableau:
Nom de la colonne | Utiliser | Notes |
---|---|---|
ACCOUNT_NAME
|
Nom du compte pour le type de compte qui est la source de ce contact brut.
Par exemple, le nom d'un compte Google correspond à l'une des adresses Gmail du propriétaire de l'appareil. Pour en savoir plus, consultez l'entrée suivante concernant ACCOUNT_TYPE .
|
Le format de ce nom est spécifique au type de compte. Il ne s'agit pas nécessairement d'une adresse e-mail. |
ACCOUNT_TYPE
|
Type de compte à l'origine de ce contact brut. Par exemple, le type de compte d'un compte Google est com.google . Indiquez toujours le type de compte avec un identifiant de domaine pour un domaine que vous possédez ou contrôlez. Cela garantit que votre type de compte est unique.
|
Un type de compte qui propose des données de contact est généralement associé à un adaptateur de synchronisation qui se synchronise avec le fournisseur de contacts. |
DELETED
|
Indicateur "deleted" pour un contact brut. | Cet indicateur permet au fournisseur de contacts de conserver la ligne en interne jusqu'à ce que les adaptateurs de synchronisation puissent la supprimer de leurs serveurs, puis de la supprimer définitivement du dépôt. |
Notes
Voici quelques remarques importantes concernant le tableau 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
, dans une ligneContactsContract.CommonDataKinds.StructuredName
. Un contact brut ne comporte qu'une seule ligne de ce type dans le tableauContactsContract.Data
. -
Attention:Pour utiliser les données de votre propre compte dans une ligne de contact brute, vous devez d'abord l'enregistrer auprès de
AccountManager
. Pour ce faire, invitez les utilisateurs à ajouter le type de compte et leur nom de compte à la liste des comptes. Si vous ne le faites pas, le fournisseur de contacts supprimera automatiquement votre ligne de contact brute.Par exemple, si vous souhaitez que votre application conserve les données de contact 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 l'inviter à ajouter le type et/ou le nom. Les types et noms de comptes sont décrits plus en détail dans la section suivante.
Sources des données brutes des contacts
Pour comprendre le fonctionnement des contacts bruts, prenons l'exemple de l'utilisateur "Emily Dickinson", qui a défini trois comptes utilisateur sur son appareil:
emily.dickinson@gmail.com
emilyd@gmail.com
- Compte Twitter "belle_of_amherst"
Cet utilisateur a activé la synchronisation des contacts pour chacun de ces trois comptes dans les paramètres Comptes.
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". Plus tard, elle se connecte à Gmail en tant que emilyd@gmail.com
et envoie un e-mail à "Thomas Higginson", ce qui l'ajoute automatiquement en tant que contact. Elle suit également "colonel_tom" (ID Twitter de Thomas Higginson) sur Twitter.
Le fournisseur de contacts crée trois contacts bruts à la suite de ce travail:
-
Un contact brut pour "Thomas Higginson" associé à
emily.dickinson@gmail.com
. Le type de compte utilisateur est Google. -
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 à un nom précédent, car la personne a été ajoutée pour un autre compte utilisateur. - 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. Cela permet à un seul contact brut d'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 contact brute pour Thomas Higginson associée au compte Google emilyd@gmail.com
) a 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 toutes les deux au contact brut.
Notez que différents types de données sont stockés dans cette seule table. Les lignes de nom à afficher, numéro de téléphone, adresse e-mail, adresse postale, photo et détails du site Web se trouvent toutes dans le tableau ContactsContract.Data
. Pour faciliter cette gestion, 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 de 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 de nom générique a une signification différente en fonction du 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
-
Le type de 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 n'importe quelle application ou adaptateur de synchronisation compatible avec le fournisseur de contacts. -
IS_PRIMARY
-
Si ce type de ligne de données peut se produire plusieurs fois pour un contact brut, la colonne
IS_PRIMARY
signale la ligne de données contenant les données principales pour le type. Par exemple, si l'utilisateur appuie de manière prolongée sur un numéro de téléphone d'un contact et sélectionne Définir comme valeur 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
Il existe 15 colonnes génériques nommées DATA1
à DATA15
qui sont généralement disponibles, ainsi que quatre colonnes génériques supplémentaires de SYNC1
à SYNC4
qui ne doivent être utilisées que par les adaptateurs de synchronisation. Les constantes de nom de colonne génériques fonctionnent toujours, quel que soit le type de données de la ligne.
La colonne DATA1
est indexée. Le fournisseur de contacts utilise toujours cette colonne pour les données que le fournisseur s'attend à être 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 des 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, le fournisseur de contacts fournit également des constantes de nom de colonne spécifiques au type, définies dans des sous-classes de ContactsContract.CommonDataKinds
. Les constantes donnent simplement un nom de constante différent au même nom de colonne, 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
ayant le 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 au tableau ContactsContract.Data
à l'aide d'une ligne contenant l'un des types MIME prédéfinis du fournisseur. Vous risqueriez alors 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 de les utiliser 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 à un type "superposent" les 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 les plus couramment utilisées, en fonction du type:
Classe de mappage | 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 contact brutes de tous les types et noms de comptes pour former un contact. Cela permet d'afficher et de modifier toutes les données qu'un utilisateur a collectées pour une personne. Le fournisseur de contacts gère la création de nouvelles lignes de contact et l'agrégation de 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 contact sont en lecture seule.
Remarque:Si vous essayez d'ajouter un contact au fournisseur de contacts avec un insert()
, une exception UnsupportedOperationException
s'affiche. Si vous essayez de mettre à jour une colonne listée comme "en lecture seule", la mise à jour est ignorée.
Le fournisseur de contacts crée un contact en réponse à l'ajout d'un contact brut qui ne correspond à aucun contact existant. Le fournisseur procède également à cette opération si les données d'un contact brut existant changent de sorte qu'elles ne correspondent plus au contact auquel elles étaient précédemment associées. Si une application ou un adaptateur de synchronisation crée un contact brut qui correspond à un contact existant, ce nouveau contact brut est agrégé avec le contact existant.
Le fournisseur de contacts associe une ligne de contact à ses lignes de contact brutes à l'aide de la colonne _ID
de la ligne de contact dans le tableau Contacts
. La colonne CONTACT_ID
de la table des 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 automatiquement les contacts, il peut modifier la valeur _ID
d'une ligne de contact en réponse à une agrégation ou une synchronisation. Même si cela se produit, l'URI de contenu CONTENT_LOOKUP_URI
combiné au LOOKUP_KEY
du contact continuera de pointer vers la ligne du contact. Vous pouvez donc utiliser LOOKUP_KEY
pour gérer les liens vers les contacts "favoris", etc. Cette colonne a son propre format, qui n'a aucun lien avec celui de la colonne _ID
.
La figure 3 montre comment les trois tables principales sont liées les unes aux autres.
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 nombre limité de champs de données et de méthodes de contact est obsolète.
Dans les conditions mentionnées, le système efface périodiquement 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:
De plus, les champs suivants ne renvoient plus de contacts fréquents. Notez que certains de ces champs n'influencent le classement des contacts que lorsqu'ils 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
(affecte uniquement les types de données Email, Phone, Callable et Contactables) -
ENTERPRISE_CONTENT_FILTER_URI
(n'affecte que les types de données Adresse e-mail, Téléphone et Appelable)
Si vos applications accèdent à ces champs ou API ou les mettent à jour, utilisez des méthodes alternatives. Par exemple, vous pouvez répondre à certains cas d'utilisation en utilisant des fournisseurs de contenu privés ou d'autres données stockées dans votre application ou vos systèmes backend.
Pour vérifier que la fonctionnalité de votre application n'est pas affectée par ce changement, 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 des adaptateurs de synchronisation
Les utilisateurs saisissent les données de contact directement sur l'appareil, mais les données sont également transmises au fournisseur de contacts à partir de 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.
Sur 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 prendre en charge plusieurs noms de compte pour ce type. Les types et noms de comptes sont décrits brièvement dans la section Sources des données de contacts brutes. Les définitions suivantes offrent plus de détails et décrivent comment le type et le nom de compte sont liés aux 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
. - Nom du compte
- Identifie un compte ou une connexion spécifique pour un type de compte. Les comptes Google Contacts sont identiques aux comptes Google, qui utilisent une adresse e-mail comme nom de compte. D'autres services peuvent utiliser un nom d'utilisateur composé d'un seul mot ou un ID numérique.
Les types de comptes n'ont pas besoin d'être uniques. Un utilisateur peut configurer plusieurs comptes Google Contacts et télécharger ses données dans Contacts Provider. Cela peut se produire s'il a un ensemble de contacts personnels pour un compte personnel et un autre pour le travail. 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 au flux de données sur les personnes. Dans la zone "adaptateurs de synchronisation", chaque adaptateur est associé à son type de compte.
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>
comme<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>
comme<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 associées sont abordés dans la section suivante, Le profil utilisateur.
N'oubliez pas que les données de contact de l'utilisateur sont personnelles et sensibles. Les utilisateurs sont soucieux de leur confidentialité et ne veulent 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 aux données de contact de l'utilisateur n'est pas claire, il peut donner une mauvaise note à votre application ou refuser tout simplement de l'installer.
Profil utilisateur
La table ContactsContract.Contacts
ne comporte qu'une seule ligne contenant les données de profil de l'utilisateur de l'appareil. Ces données décrivent l'user
de l'appareil plutôt que l'un des contacts de l'utilisateur. La ligne "Contacts du profil" est associée à une ligne de contacts brute pour chaque système qui utilise un profil.
Chaque ligne de contact brut du profil peut comporter plusieurs lignes de données. Les constantes d'accès 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 les autorisations android.Manifest.permission#READ_PROFILE et android.Manifest.permission#WRITE_PROFILE pour l'accès en lecture et en écriture, respectivement.
N'oubliez pas que vous devez considérer le profil d'un utilisateur comme 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. Assurez-vous d'indiquer à l'utilisateur pourquoi vous avez besoin d'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 spécifiez aucun critère de sélection. Vous pouvez également utiliser cet URI de contenu comme URI de base pour récupérer des contacts ou des données brutes pour le 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'elles correspond au profil utilisateur, testez la colonne IS_USER_PROFILE
de la ligne. Cette colonne est définie sur "1" si le contact correspond au 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 contacts dans le dépôt. Ces métadonnées sur le dépôt sont stockées à divers endroits, y compris dans les lignes des tables "Contacts bruts", "Données" et "Contacts", la table ContactsContract.Settings
et la table ContactsContract.SyncState
. Le tableau suivant présente l'impact de chacune de ces métadonnées:
Tableau | Colonne | Valeurs | Signification |
---|---|---|---|
ContactsContract.RawContacts |
DIRTY |
"0" : pas de modification depuis la dernière synchronisation. |
Indique les contacts bruts qui ont été modifiés sur l'appareil et qui doivent être synchronisés avec le serveur. La 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 tables de données ou de contacts brutes doivent toujours ajouter la chaîne |
"1" : modifié depuis la dernière synchronisation. Doit être resynchronisé 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. | Le fournisseur de contacts 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 de manière unique ce contact brut au 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'ID unique du serveur pour le contact brut. 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 être stable lors des synchronisations:
|
ContactsContract.Groups |
GROUP_VISIBLE |
"0" : les contacts de ce groupe ne doivent pas être visibles dans les UI des applications Android. | Cette colonne est destinée à la compatibilité avec les serveurs qui permettent à un utilisateur de masquer des contacts dans certains groupes. |
"1" : les contacts de ce groupe peuvent être visibles dans les UI des applications. | |||
ContactsContract.Settings |
UNGROUPED_VISIBLE |
"0" : pour ce compte et ce type de compte, les contacts qui n'appartiennent pas à un groupe sont invisibles pour les UI d'application Android. |
Par défaut, les contacts sont invisibles si aucun de leurs contacts bruts n'appartient à un groupe (l'appartenance à un groupe pour un contact brut est indiquée par une ou plusieurs lignes ContactsContract.CommonDataKinds.GroupMembership dans la table ContactsContract.Data ).
En définissant cet indicateur dans la ligne du tableau ContactsContract.Settings pour un type de compte et un compte, vous pouvez forcer la visibilité des contacts sans groupes.
Cet indicateur permet, entre autres, d'afficher les contacts des serveurs qui n'utilisent pas de groupes.
|
"1" : pour ce compte et ce type de compte, les contacts n'appartenant à aucun groupe sont visibles par les interfaces utilisateur des applications. | |||
ContactsContract.SyncState |
(toutes) | Utilisez ce tableau pour stocker les métadonnées de votre adaptateur de synchronisation. | Cette table vous permet de stocker de manière persistante l'état de synchronisation et d'autres données liées à la synchronisation 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, en mettant l'accent sur les points suivants:
- Requêtes d'entités.
- Modification par lots.
- Récupération et modification avec des intents
- Intégrité des données
Les modifications apportées à partir d'un adaptateur de synchronisation sont également expliquées plus en détail dans la section Adaptateurs de synchronisation du fournisseur de contacts.
Interroger des entités
Étant donné que les tables du fournisseur de contacts sont organisées de manière 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
pour une seule ligne ContactsContract.Contacts
ou toutes les lignes ContactsContract.CommonDataKinds.Email
pour une seule ligne ContactsContract.RawContacts
. Pour faciliter cette opération, le fournisseur de contacts propose des constructions d'entités, qui agissent comme des jointures de bases de données entre les tables.
Une entité est comme une table composée de colonnes sélectionnées d'une table parente et de sa table enfant.
Lorsque vous interrogez une entité, vous fournissez une projection et des critères de recherche en fonction des colonnes disponibles pour l'entité. Le résultat est un Cursor
contenant une ligne pour chaque ligne de 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 associés à 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 toutes les données de contact d'un contact ou d'un contact brut en une seule fois, au lieu d'avoir à interroger d'abord la table parente pour obtenir un ID, puis à interroger la table enfant avec cet ID. De plus, le fournisseur de contacts traite une requête sur une entité dans une seule transaction, ce qui garantit que les données récupérées sont cohérentes en interne.
Remarque:Une entité ne contient généralement pas toutes les colonnes de la table parente et de la table enfant. Si vous tentez d'utiliser un nom de colonne qui ne figure pas dans la liste des constantes de nom de colonne de l'entité, vous obtenez une erreur Exception
.
L'extrait de code suivant montre comment récupérer toutes les lignes de contact brutes d'un contact. L'extrait fait partie d'une application plus vaste qui comporte deux activités : "main" et "detail". L'activité principale affiche une liste de lignes de contact. Lorsque l'utilisateur en sélectionne une, l'activité envoie son ID à l'activité détaillée. L'activité détaillée 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 tiré 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 Cursor
avec les résultats de la requête. Dans votre propre application, vous pouvez obtenir les données de cette Cursor
pour les afficher ou les utiliser plus loin.
Modification par lot
Dans la mesure du possible, vous devez insérer, mettre à jour et supprimer des données dans le fournisseur de contacts en "mode par lot", en créant un ArrayList
d'objets ContentProviderOperation
et en appelant applyBatch()
. Étant donné que le fournisseur de contacts effectue toutes les opérations d'une applyBatch()
dans une seule transaction, vos modifications ne laisseront jamais le dépôt de contacts dans un état incohérent. 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 Contacts de l'appareil plutôt que de gérer la modification dans votre application. Cette procédure est décrite plus en détail dans 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 entraîne une mauvaise expérience utilisateur globale. Pour organiser toutes les modifications que vous souhaitez effectuer dans le moins de listes distinctes possible et, en même temps, les empêcher 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 laisser les autres processus s'exécuter et met fin à la transaction en cours. Lorsque le fournisseur redémarre, il poursuit l'opération suivante dans ArrayList
et démarre une nouvelle transaction.
Les points de rendement génèrent plusieurs transactions par appel à applyBatch()
. Par conséquent, vous devez définir un point de rendement pour la dernière opération d'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 pour la dernière opération d'un ensemble de lignes lié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 réussissent ou échouent en tant qu'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 qu'un sous-ensemble d'opérations est atomique.
Références de modification
Lorsque vous insérez une nouvelle ligne de contact brute et les lignes de données qui lui sont associées en tant qu'ensemble d'objets ContentProviderOperation
, vous devez associer les lignes de données à la ligne de contact brute 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
dispose de 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 du tableau que vous modifiez.
-
previousResult
-
Indice de 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 de résultats intermédiaires. La valeurpreviousResult
est l'index de l'un de ces résultats, qui est récupéré et stocké avec la valeurkey
. Vous pouvez ainsi insérer un nouvel enregistrement de contact brut et récupérer sa valeur_ID
, puis créer une "référence arrière" à la valeur lorsque vous ajoutez une ligneContactsContract.Data
.L'ensemble du tableau de résultats est créé lors du premier appel de
applyBatch()
, avec une taille égale à celle de l'ArrayList
des objetsContentProviderOperation
que vous fournissez. Toutefois, tous les éléments du tableau de résultats 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 exceptionException
.
Les extraits de code suivants montrent comment insérer un nouveau contact et des données brutes par lot. Ils 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 de contact à partir de l'UI. À 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 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 de nom à afficher, de téléphone et d'adresse e-mail.
Chaque objet de compilateur d'opérations utilise withValueBackReference()
pour obtenir le RAW_CONTACT_ID
. Les points de référence reviennent à 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'e-mail est signalé par withYieldAllowed()
, ce 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 d'implémenter un contrôle de la concurrence 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, vous devez appliquer la transaction, puis rechercher d'autres modifications qui ont pu être apportées en même temps. Si vous constatez qu'une modification incohérente s'est produite, annulez votre transaction et réessayez.
Le contrôle de simultanéité optimiste est utile pour un appareil mobile sur lequel 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. Étant donné que le verrouillage n'est pas utilisé, aucun temps n'est perdu à définir des verrous ni à attendre que d'autres transactions libèrent leurs verrous.
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, ainsi que 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
avec le_ID
du contact brut ajouté. -
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 cet objet en tant que premier objet dans leArrayList
que vous transmettez àapplyBatch()
. - Appliquez la transaction par lot.
Si la ligne de contact brute est mise à jour par une autre opération entre le moment où vous lisez la ligne et celui où vous essayez de la modifier, l'assertion ContentProviderOperation
échoue et l'ensemble du lot d'opérations est annulé. Vous pouvez ensuite choisir de réessayer le lot ou d'effectuer une autre action.
L'extrait de code suivant montre comment créer une ContentProviderOperation
"assert" 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
Envoyer un intent à l'application Contacts de l'appareil vous permet d'accéder indirectement au fournisseur de contacts. L'intent démarre l'UI 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 la suite.
- Modifier les données d'un contact existant
- Insérer un nouveau contact brut pour l'un de ses comptes
- Supprimez un contact ou ses données.
Si l'utilisateur insère ou met à jour des données, vous pouvez d'abord les collecter, puis les envoyer dans le cadre de 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 UI ni votre propre code pour accéder au fournisseur. Vous n'avez pas non plus à demander l'autorisation de lire ou d'écrire au fournisseur. L'application Contacts de l'appareil peut vous déléguer l'autorisation de lecture d'un contact. Comme vous apportez des modifications au fournisseur via une autre application, vous n'avez pas besoin d'autorisations d'é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 des fournisseurs de contenu, dans la section "Accès aux données via les 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
:
Tâche | Action | Données | Type MIME | Notes |
---|---|---|---|---|
Sélectionner un contact dans une liste | ACTION_PICK |
Au choix :
|
Non utilisé |
Affiche une liste de contacts bruts ou une liste de données 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 est le type MIME d'un ensemble de contacts bruts.
|
Affiche l'écran Ajouter un contact de l'application Contacts de l'appareil. Les valeurs supplémentaires que vous ajoutez à l'intent sont affichées. 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 la valeur, appelez getData() .
|
Modifier un contact | ACTION_EDIT |
CONTENT_LOOKUP_URI pour le contact. L'activité de l'éditeur permet à l'utilisateur de modifier toutes les données associées à ce contact.
|
Contacts.CONTENT_ITEM_TYPE , un seul contact. |
Affiche l'écran "Modifier un 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 pouvant é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 choisir un contact à modifier ou en ajouter un. L'écran de modification ou d'ajout s'affiche, en fonction du choix de l'utilisateur, et les données supplémentaires que vous transmettez dans 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.
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. De plus, si vous envoyez un nom et que l'utilisateur choisit d'effectuer une modification, l'application de contacts affiche ce nom en remplaçant la valeur précédente. Si l'utilisateur ne le remarque 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 à l'aide d'un intent. Pour supprimer un contact brut, utilisez plutôt ContentResolver.delete()
ou ContentProviderOperation.newDelete()
.
L'extrait de code suivant montre comment créer et envoyer un intent qui insère un nouveau contact brut et des données:
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 à ce qu'elles soient correctes et à jour, le fournisseur de contacts applique des règles strictes en matière d'intégrité des données. Il est de votre responsabilité de vous conformer à ces règles lorsque vous modifiez les données de contact. Voici les règles importantes:
-
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
ne sera pas visible dans l'application Contacts de l'appareil et peut entraîner des problèmes avec les adaptateurs de synchronisation. - Modifiez uniquement les données des contacts bruts dont vous êtes le propriétaire.
- N'oubliez pas que le fournisseur de contacts gère généralement les données de plusieurs types de comptes/services en ligne différents. 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 associées à un type de compte et à un nom 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 colonnes, 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 le tableau 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 aux noms de colonnes par défaut. Dans l'application Contacts de l'appareil, les données de vos lignes sont affichées, mais elles 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'éditeur 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 contact entre un appareil et un service en ligne. Cela permet aux utilisateurs de télécharger des données existantes sur un nouvel appareil et d'importer les données existantes vers un nouveau compte. La synchronisation garantit également que les utilisateurs disposent des dernières données à portée de main, quelle que soit la source des ajouts et des modifications. Un autre avantage de la synchronisation est qu'elle rend les données de contact 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.
- Planifier et exécuter la synchronisation en fonction des préférences 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 il peut gérer plusieurs noms de compte pour le même service. Le framework autorise également plusieurs adaptateurs de synchronisation pour le même service et le même fournisseur.
Synchroniser les classes et les fichiers de l'adaptateur
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 à connaître l'adaptateur de synchronisation à partir des éléments de votre fichier manifeste d'application et d'un fichier XML spécial pointé par 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 de manière unique l'adaptateur. L'adaptateur de synchronisation ne devient actif que lorsque l'utilisateur ajoute un compte pour le type de compte de l'adaptateur de synchronisation 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 synchroniser 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 ont tous le même type de compte com.google
. Lorsque les utilisateurs ajoutent un compte Google à leurs appareils, tous les adaptateurs de synchronisation installés pour les services Google sont listés ensemble. Chaque adaptateur de synchronisation listé se synchronise avec un fournisseur de contenu différent sur l'appareil.
Étant donné que la plupart des services exigent 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 d'adaptateur de synchronisation et souvent utilisé en conjonction avec celui-ci. Le framework d'authentification utilise des authentificateurs de plug-in 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.
- Examine la réponse du service.
Si le service accepte les identifiants, l'authentificateur peut les stocker pour une utilisation ultérieure. Grâce au framework d'authentification par plug-in, AccountManager
peut fournir l'accès à tous les jetons d'authentification qu'un authentificateur accepte et choisit d'exposer, tels que les jetons d'authentification OAuth2.
Bien que l'authentification ne soit pas obligatoire, la plupart des services de contacts l'utilisent. Toutefois, vous n'êtes pas obligé d'utiliser le framework d'authentification Android pour procéder à 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:
-
Composant
Service
qui répond aux requêtes du système pour se lier à l'adaptateur de synchronisation. -
Lorsque le système souhaite exécuter une synchronisation, il appelle la méthode
onBind()
du service pour obtenir unIBinder
pour l'adaptateur de synchronisation. Cela permet au système d'effectuer des appels interprocessus aux méthodes de l'adaptateur. -
Adaptateur de synchronisation réel, implémenté en tant que sous-classe concrète de
AbstractThreadedSyncAdapter
. -
Cette classe se charge de télécharger les données à partir du serveur, d'importer les données à partir de 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. -
Sous-classe de
Application
. -
Cette classe agit comme une usine 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 pour l'authentification des utilisateurs. -
AccountManager
démarre ce service pour lancer 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 pour 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 en fonction de 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 dans le 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>
du service d'adaptateur de synchronisation pointe vers le fichier XMLres/xml/syncadapter.xml
. Ce fichier spécifie à son tour un URI pour le service Web qui sera synchronisé avec le fournisseur de contacts, ainsi qu'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 à son tour le type de compte compatible avec cet authentificateur, ainsi que les ressources d'UI qui s'affichent pendant le 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, ou lire les données de flux de ces tables et les afficher dans votre propre application, ou les deux. Grâce à ces fonctionnalités, vous pouvez intégrer vos services et applications de réseaux sociaux à l'expérience de réseautage 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 est associé à la valeur _ID
du contact brut. Le type de compte 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. Vous devez insérer les colonnes d'identifiant suivantes lorsque vous insérez un élément de flux :
- 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 des 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, soit le contenu publié par la source de l'élément, soit une description d'une action ayant généré l'élément de flux. Cette colonne peut contenir tout formatage et toute image de ressource intégrée pouvant être affichés par
fromHtml()
. Le fournisseur peut tronquer ou elliptiser le contenu long, mais il essaiera d'éviter de couper 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, sous la forme de millisecondes depuis l'epoch. Les applications qui insèrent ou mettent à jour des éléments de flux sont chargées de gérer cette colonne. Elle n'est pas gérée automatiquement par le fournisseur de contacts.
Pour afficher des informations d'identification pour vos éléments de flux, utilisez les éléments android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL et android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE pour associer des ressources dans votre application.
La table android.provider.ContactsContract.StreamItems contient également les colonnes android.provider.ContactsContract.StreamItemsColumns#SYNC1 à android.provider.ContactsContract.StreamItemsColumns#SYNC4 pour l'utilisation exclusive des adaptateurs de synchronisation.
Photos du flux sur les 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 fait référence aux valeurs de la colonne _ID
de la table android.provider.ContactsContract.StreamItems. Les références de photos sont stockées dans la table, dans les colonnes suivantes:
- Colonne android.provider.ContactsContract.StreamItemPhotos#PHOTO (un BLOB).
- Représentation binaire de la photo, redimensionnée par le fournisseur pour le stockage et l'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 (les deux étant décrits dans les points suivants) pour stocker des photos dans un fichier. Cette colonne contient désormais une vignette de la photo, qui peut être lue.
- android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
-
Identificateur 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 gestionnaire du fichier photo. - android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
-
Un 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 gestionnaire du fichier photo.
Utiliser les tables de flux sur les réseaux sociaux
Ces tables fonctionnent de la même manière que les autres tables principales du fournisseur de contacts, à l'exception des points suivants:
- Ces tables nécessitent des autorisations d'accès supplémentaires. Pour 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 libère de l'espace pour les nouvelles lignes d'éléments de flux en supprimant automatiquement les lignes ayant le plus ancien android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP. Pour obtenir 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éfini sur
null
. La requête renvoie un curseur contenant une seule ligne, avec la colonne unique android.provider.ContactsContract.StreamItems#MAX_ITEMS.
La classe android.provider.ContactsContract.StreamItems.StreamItemPhotos définit une sous-table de android.provider.ContactsContract.StreamItemPhotos contenant les lignes de photo d'un seul élément de flux.
Interactions dans le flux sur les réseaux sociaux
Les données de flux de réseau social gérées par le fournisseur de contacts, en association avec l'application de contacts de l'appareil, constituent un moyen efficace de connecter votre système de réseautage social aux 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 tableaux android.provider.ContactsContract.StreamItems et android.provider.ContactsContract.StreamItemPhotos pour une utilisation ultérieure.
- En plus de la 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 les photos haute résolution et les éléments de flux les plus récents pour le contact.
- En enregistrant une notification auprès de l'application Contacts de l'appareil et du fournisseur de contacts, vous pouvez recevoir un intent lorsqu'un contact est consulté, puis mettre à jour son état à partir de votre service. Cette approche peut être 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 lorsqu'ils consultent le contact dans l'application Contacts de l'appareil. Pour ce faire, vous devez utiliser la fonctionnalité "Inviter un contact", qui s'accompagne d'une activité qui ajoute un contact existant à votre réseau et d'un fichier XML qui fournit à l'application de contacts de l'appareil et au fournisseur de contacts les détails de votre application.
La synchronisation régulière des éléments de flux avec le fournisseur de contacts est identique aux autres synchronisations. Pour en savoir plus sur la synchronisation, consultez la section Adaptateurs de synchronisation du fournisseur de contacts. L'enregistrement des notifications et l'invitation de contacts sont abordés dans les deux sections suivantes.
S'inscrire pour gérer les vues sur les réseaux sociaux
Pour enregistrer votre adaptateur de synchronisation afin qu'il reçoive des notifications lorsque l'utilisateur consulte un contact géré par votre adaptateur de synchronisation:
-
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 de notification, utilisez une classe qui étendIntentService
pour 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 établir une liaison avec 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 de flux ou 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 de l'utilisateur sur un élément de flux dans l'application de 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 de contacts de l'appareil. -
Pour enregistrer l'une de vos activités afin de gérer le clic de l'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 laquelle 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 Contacts de l'appareil pour inviter un contact sur votre site de réseau social. Vous pouvez plutôt demander à l'application 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. Il utilise la syntaxe 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 dans:
res/xml/contacts.xml
peut contenir:
<ContactsDataKind>
Description :
Déclarer des composants Android et des libellés d'interface utilisateur permettant aux utilisateurs d'inviter l'un de leurs contacts à rejoindre un réseau social, de les informer lorsqu'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 qui s'affiche 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 qui doit recevoir des notifications lorsque l'utilisateur consulte un contact. Cette notification est envoyée par l'application de contacts de l'appareil. Elle permet à votre application de différer les opérations gourmandes en 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 les plus récents de son flux de réseau social. Pour en savoir plus, consultez la section Interactions avec le flux social.
viewGroupActivity
- Nom de classe complet d'une activité dans 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'UI de cette activité s'affiche.
viewGroupActionLabel
-
Libellé affiché par l'application Contacts pour un contrôle d'interface utilisateur permettant à l'utilisateur d'afficher les groupes de 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 Contacts de l'appareil lance lorsque l'utilisateur clique sur un élément de flux pour un contact brut.
viewStreamItemPhotoActivity
- Nom de classe complet d'une activité de votre application que l'application Contacts de l'appareil lance lorsque l'utilisateur clique sur une photo dans l'élément de flux pour 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'UI de l'application Contacts. Il utilise la syntaxe suivante:
<ContactsDataKind android:mimeType="MIMEtype" android:icon="icon_resources" android:summaryColumn="column_name" android:detailColumn="column_name">
contenu dans:
<ContactsAccountType>
Description :
Utilisez cet élément pour demander à l'application de contacts d'afficher le contenu d'une ligne de données personnalisées 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
-
Le type MIME personnalisé que vous avez défini pour l'un de vos types de lignes de données personnalisées dans le tableau
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
- Une 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 la colonne pour la première des deux valeurs extraites de la ligne de données. La valeur apparaît en tant que première ligne de l'entrée pour cette ligne de données. La première ligne est destinée à servir de résumé des données, mais elle est facultative. Voir aussi android:detailColumn.
android:detailColumn
-
Nom de la colonne pour la deuxième des deux valeurs récupérées à partir de la ligne de données. La valeur correspond à la deuxième ligne de l'entrée pour cette ligne de données. Voir aussi
android:summaryColumn
.
Fonctionnalités supplémentaires de Contacts Provider
En plus des principales fonctionnalités décrites dans les sections précédentes, le fournisseur de contacts propose les fonctionnalités utiles suivantes pour travailler avec les données de contact:
- Groupes de contacts
- Fonctionnalités photo
Groupes de contacts
Le fournisseur de contacts peut éventuellement étiqueter des collections de contacts associés avec des données de groupe. Si le serveur associé à un compte utilisateur souhaite gérer des groupes, l'adaptateur de synchronisation du 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 contact au serveur, puis le placent dans un nouveau groupe, l'adaptateur de synchronisation doit ajouter le nouveau groupe au tableau 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 ajoutera les 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
que le fournisseur de contacts ajoute pour le compte. Dans cette ligne, définissez la valeur de la colonne Settings.UNGROUPED_VISIBLE
sur 1. Dans ce cas, le fournisseur de contacts affichera toujours vos données de contacts, même si vous n'utilisez pas de groupes.
Photos des contacts
La table ContactsContract.Data
stocke les photos sous forme de lignes avec le 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 de la photo principale d'un contact, qui est la photo principale du contact brut principal du contact. De même, la classe ContactsContract.RawContacts.DisplayPhoto
définit une sous-table 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 aucune classe pratique pour récupérer la vignette principale d'un contact brut, mais vous pouvez envoyer une requête à la table ContactsContract.Data
, en sélectionnant le _ID
, le Photo.CONTENT_ITEM_TYPE
et la colonne IS_PRIMARY
du contact brut pour trouver la ligne de photo principale du contact brut.
Les données de flux sur les réseaux sociaux concernant une personne peuvent également inclure des photos. Ils sont stockés dans la table android.provider.ContactsContract.StreamItemPhotos, décrite plus en détail dans la section Photos du flux social.