Contacts Provider

Le fournisseur de contacts 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 visibles dans l'application Contacts de l'appareil. Vous pouvez également accéder à ses données dans vos propres l'application et de transférer des données entre l'appareil et les services en ligne. Le fournisseur s'adapte un large éventail de sources de données et essaie de gérer autant de données que possible pour chaque personne, avec et cela rend son organisation 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 des données et modification.

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
  • Comment écrire un adaptateur pour synchroniser les données entre votre serveur et 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 :

Figure 1 : Structure de la table des fournisseurs de contacts.

Les trois tables sont communément appelées par les noms de leurs classes contractuelles. Les cours Vous pouvez définir des constantes pour les URI de contenu, les noms de colonnes et les valeurs de colonne utilisés par les tables:

ContactsContract.Contacts tableau
Lignes représentant différentes personnes, sur la base d'agrégations de lignes de contacts brutes
ContactsContract.RawContacts table
Lignes contenant un résumé des données d'une personne, spécifique à un compte utilisateur et à un type.
ContactsContract.Data table
Lignes contenant les détails des contacts bruts, tels que les adresses e-mail ou les numéros de téléphone

Les autres tables représentées par des classes de contrat dans ContactsContract sont des tables auxiliaires que le fournisseur de contacts utilise pour gérer ses opérations ou l'assistance 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. Comme le fournisseur de contacts autorise plusieurs services en ligne en tant que source d'une personne, le fournisseur de contacts autorise plusieurs contacts bruts pour une 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. Elles sont stockées dans un ou plusieurs lignes dans 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 :

Tableau 1. Colonnes importantes des contacts bruts.

Nom de la colonne Utiliser Notes
ACCOUNT_NAME Nom du type de compte qui est la source de ce contact brut. Par exemple, le nom d'un compte Google correspond à l'une des adresses Gmail du propriétaire de l'appareil des adresses IP externes. Reportez-vous à la prochaine entrée pour ACCOUNT_TYPE pour plus des informations. 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 compte type de compte Google est com.google. Évaluez toujours votre type de compte par l'identifiant d'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" (supprimé) 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

Vous trouverez ci-dessous des remarques importantes concernant le Table ContactsContract.RawContacts:

  • Le nom d'un contact brut n'est pas stocké dans sa ligne dans ContactsContract.RawContacts. Au lieu de cela, il est stocké dans la table ContactsContract.Data, dans une ligne ContactsContract.CommonDataKinds.StructuredName. Un contact brut ne comporte qu'une seule ligne de ce type dans le tableau ContactsContract.Data.
  • Attention : Pour utiliser les données de votre propre compte dans une ligne de contact brute, vous devez : être d'abord enregistré auprès de AccountManager. Pour ce faire, invitez aux utilisateurs d'ajouter le type de compte et le nom de leur compte à la liste des comptes. Si vous ne le faites pas, le fournisseur de contacts supprimera automatiquement votre ligne de contact brute.

    Par exemple, si vous souhaitez que votre application gère 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 est becky.sharp@dataservice.example.com, l'utilisateur doit d'abord ajouter le "type" (com.example.dataservice) et le "nom" (becky.smart@dataservice.example.com) du compte avant que votre application puisse ajouter des lignes de contact brutes. Vous pouvez expliquer cette exigence à l'utilisateur dans la documentation, ou vous pouvez demander à l' pour 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 de données de contacts brutes

Pour comprendre le fonctionnement des contacts bruts, prenons l'utilisateur « Emily Dickinson ». qui a ce qui suit : trois comptes utilisateur définis 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, ouvertures Contacts, puis ajoute "Thomas Higginson". Plus tard, elle se connecte à Gmail en tant que emilyd@gmail.com et envoie un e-mail à "Thomas Higginson", qui lui-même l'ajoute comme contact. Elle suit également "colonel_tom". (identifiant Twitter de Thomas Higginson) sur Twitter

Le fournisseur de contacts crée trois contacts bruts à la suite de ce travail :

  1. Un contact brut pour "Thomas Higginson" associé à emily.dickinson@gmail.com. Le type de compte utilisateur est Google.
  2. 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.
  3. Un troisième contact brut pour "Thomas Higginson" associé à "belle_of_amherst". Le type de compte utilisateur est Twitter.

Données

Comme indiqué précédemment, les données d'un contact brut sont stockées dans un Ligne ContactsContract.Data associée aux coordonnées brutes du contact Valeur _ID. Cela permet à un seul contact brut d'avoir plusieurs instances du même tels que des adresses e-mail ou des numéros de téléphone. Par exemple, si "Thomas Higginson" pour emilyd@gmail.com (ligne de contact brute pour Thomas Higginson) associé au compte Google emilyd@gmail.com) possède une adresse e-mail personnelle de thigg@gmail.com et une adresse e-mail professionnelle de thomas.higginson@gmail.com, le fournisseur de contacts stocke les deux adresses e-mail et les associe au contact brut.

Notez que différents types de données sont stockés dans cette seule table. Nom à afficher, le numéro de téléphone, l'adresse e-mail, l'adresse postale, la photo et les informations détaillées sur le site Web. ContactsContract.Data. Pour vous aider, La table ContactsContract.Data contient des 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
Type de données stockées sur cette ligne, exprimé sous la forme d'un type MIME personnalisé. Contacts Provider 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 apparaître plusieurs fois pour un contact brut, le Indicateurs de colonne IS_PRIMARY la ligne de données qui contient 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 colonne IS_PRIMARY de la ligne ContactsContract.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 disponibles pour tous les utilisateurs, ainsi que quatre autres options génériques colonnes SYNC1 à SYNC4 qui ne doivent être utilisées que pour la synchronisation adaptateurs. Les constantes de nom de colonne génériques fonctionnent toujours, quel que soit le type de les données de la ligne.

La colonne DATA1 est indexée. Le fournisseur de contacts utilise toujours cette colonne pour les données attendues par le fournisseur seront la cible la plus fréquente d'une requête. Par exemple : dans une ligne d'adresse e-mail, cette colonne contient l'adresse e-mail réelle.

Par convention, la colonne DATA15 est réservée au stockage de données BLOB (Binary Large Object), telles que des miniatures de photos.

Noms de colonnes spécifiques à un type

Pour faciliter l'utilisation des colonnes d'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 Constantes de nom de colonne spécifiques au type pour une ligne ContactsContract.Data dont le type MIME est Email.CONTENT_ITEM_TYPE La classe contient la constante ADDRESS pour la colonne d'adresse e-mail. La valeur réelle de ADDRESS correspond à "data1", ce qui signifie identique au nom générique de la colonne.

Attention:N'ajoutez pas vos propres données personnalisées au ContactsContract.Data à l'aide d'une ligne comportant l'un des aux types MIME prédéfinis du fournisseur. Sinon, vous risquez de perdre les données ou d'amener le fournisseur un dysfonctionnement. 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 pour définir des noms de colonnes spécifiques à un type et utiliser les colonnes comme vous le souhaitez.

La figure 2 illustre la façon dont les colonnes descriptives et les colonnes de données apparaissent dans un ContactsContract.Data ligne et la façon dont les noms de colonnes spécifiques à un type sont "superposés" les noms de colonnes génériques

Comment les noms de colonnes spécifiques au type sont mappés aux noms de colonnes génériques

Figure 2. Noms de colonnes spécifiques au type et noms de colonnes génériques.

Classes de noms de colonnes spécifiques au type

Le tableau 2 liste les classes de nom de colonne spécifiques au type les plus couramment utilisées:

Tableau 2. Classes de noms de colonnes spécifiques à un type

Classe de mappage Type de données Notes
ContactsContract.CommonDataKinds.StructuredName Données relatives au 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 des contacts et l'agrégation des contacts bruts avec une ligne de contact existante. Ni les applications ni les adaptateurs de synchronisation ne sont autorisés à ajouter des contacts, et certaines colonnes d'une ligne de contact sont en lecture seule.

Remarque:Si vous essayez d'ajouter un contact à Contacts Provider avec un insert(), vous bénéficiez de une exception UnsupportedOperationException. Si vous essayez de mettre à jour une colonne qui est répertorié 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 nouveau contact brut. qui ne correspond à aucun contact existant. Le fournisseur effectue également cette opération si une instance les données du contact changent de telle sorte qu'elles ne correspondent plus au contact auquel il était précédemment associés. Si une application ou un adaptateur de synchronisation crée un contact brut qui correspond à un contact existant, le nouveau contact brut est agrégé au 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. Colonne CONTACT_ID de la table des contacts bruts ContactsContract.RawContacts contient _ID valeurs 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. Comme le fournisseur de contacts gère les contacts automatiquement, la valeur _ID d'une ligne de contact peut être modifiée en réponse à une agrégation ou une synchronisation. Même si cela se produit, l'URI de contenu CONTENT_LOOKUP_URI combiné à LOOKUP_KEY du contact continuera pointez vers la ligne de contact, ce qui vous permet LOOKUP_KEY pour conserver les liens vers les favoris des contacts, 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 tableaux principaux sont liés les uns aux autres.

Tables principales du fournisseur de contacts

Figure 3. Relations entre les tables "Contacts", "Contacts bruts" et "Détails".

Attention : Si vous publiez votre application sur le Google Play Store ou si elle est installée sur un appareil équipé d'Android 10 (niveau d'API 29) ou version ultérieure, gardez à l'esprit qu'un ensemble limité de champs et de méthodes de données de contacts sont obsolètes.

Dans les conditions mentionnées, le système efface régulièrement les valeurs dans ces champs de données:

Les API utilisées pour définir les champs de données ci-dessus sont également obsolètes:

En outre, les champs suivants ne renvoient plus les 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.

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 le fonctionnement de votre application n'est pas affecté par ce changement, procédez comme suit : vous pouvez effacer manuellement ces champs de données. Pour ce faire, exécutez l'ADB suivant 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 leurs données de contacts directement dans l'appareil, mais ces données sont également transmises aux contacts Fournisseur des services Web via des adaptateurs de synchronisation, qui automatisent le transfert de données entre l'appareil et les services. Les adaptateurs de synchronisation s'exécutent en arrière-plan sous le contrôle du système et appellent les méthodes ContentResolver. pour gérer les données.

Dans Android, le service Web avec lequel un adaptateur de synchronisation fonctionne est identifié par un type de compte. Chaque adaptateur de synchronisation fonctionne avec un type de compte, mais il peut accepter plusieurs noms de compte pour de ce type. Les types et noms de comptes sont décrits brièvement dans la section Sources de données de contacts brutes. Les définitions suivantes proposent plus en détail et décrire le lien entre le type et le nom du compte et les adaptateurs et services de synchronisation.

Type de compte
Identifie un service dans lequel l'utilisateur a stocké des données. La plupart du temps, l'utilisateur doit s'authentifier auprès du service. Par exemple, Google Contacts est un type de compte, identifié par le code google.com. Cette valeur correspond au type de compte utilisé par AccountManager.
Nom du compte
Identifie un compte ou un identifiant de 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 ou un identifiant numérique composé d'un seul mot.

Les types de comptes ne doivent pas nécessairement être uniques. Un utilisateur peut configurer plusieurs comptes Google Contacts et télécharger leurs données vers le fournisseur de contacts ; cela peut se produire si l'utilisateur a un ensemble de des contacts personnels pour un nom de 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 un service externe.

Si vous souhaitez transférer les données de votre service à Contacts Provider, vous devez écrire vos 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 le champ portant la mention "Adaptateurs de synchronisation", chaque adaptateur est étiqueté par son type de compte.

Flux de données sur des personnes

Figure 4. Flux de données du fournisseur de contacts.

Autorisations requises

Les applications qui souhaitent accéder au fournisseur de contacts doivent demander les autorisations suivantes :

Accès en lecture à une ou plusieurs tables
READ_CONTACTS, spécifié dans AndroidManifest.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é dans AndroidManifest.xml avec le <uses-permission> en tant que <uses-permission android:name="android.permission.WRITE_CONTACTS">

Ces autorisations ne s'étendent pas aux données du profil utilisateur. Le profil utilisateur et ses autorisations requises sont abordés dans la section suivante, Profil utilisateur.

N'oubliez pas que les données de contact de l'utilisateur sont personnelles et sensibles. Les utilisateurs sont soucieux de leur confidentialité et ne veulent pas que les applications collectent des données les concernant ou concernant leurs contacts. Si vous ne comprenez pas pourquoi vous avez besoin d'une autorisation pour accéder aux données de ses contacts, il peut vous donner de mauvaises notes pour votre application ou refuser tout simplement de l'installer.

Profil utilisateur

La table ContactsContract.Contacts comporte une seule ligne contenant les données de profil de l'utilisateur de l'appareil. Ces données décrivent plutôt le user de l'appareil plusieurs contacts de l'utilisateur. La ligne des contacts du profil est associée à un profil brut de contacts pour chaque système utilisant un profil. Chaque ligne de contact brut du profil peut comporter plusieurs lignes de données. Constantes pour accéder à l'utilisateur sont disponibles dans la classe ContactsContract.Profile.

L'accès au profil utilisateur nécessite des autorisations spéciales. En plus des READ_CONTACTS et Autorisations WRITE_CONTACTS nécessaires pour lire et écrire, accéder au profil utilisateur nécessite les autorisations android.Manifest.permission#READ_PROFILE et android.Manifest.permission#WRITE_PROFILE autorisations d'accès en lecture et en écriture, respectivement.

N'oubliez pas que le profil d'un utilisateur doit être considéré comme sensible. L'autorisation android.Manifest.permission#READ_PROFILE vous permet d'accéder au compte de l'utilisateur permettant d'identifier personnellement l'utilisateur. 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 fournissez aucun critère de sélection. Vous pouvez également utiliser cet URI de contenu comme URI de base pour récupérer des données les contacts ou les données du profil. Par exemple, cet extrait de code récupère des données pour le 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 contacts et que vous souhaitez déterminer si l'une d'entre elles est le profil utilisateur, testez IS_USER_PROFILE. 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 permettent de suivre l'état des données de contact 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 les de chacun de ces éléments de métadonnées:

Tableau 3 : Métadonnées dans le fournisseur de contacts

Tableau Colonne Valeurs Signification
ContactsContract.RawContacts DIRTY "0" : n'a pas été modifié depuis la dernière synchronisation. Marque les contacts bruts qui ont été modifiés sur l'appareil et doivent être synchronisés de nouveau avec le Google Cloud. 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 bruts doivent toujours ajouter le paramètre la chaîne CALLER_IS_SYNCADAPTER à l'URI de contenu qu'ils utilisent. Cela empêche le fournisseur de marquer les lignes comme sales. Sinon, les modifications apportées à l'adaptateur de synchronisation apparaissent comme des modifications locales et sont envoyées au serveur, même si le serveur était la source de la modification.

"1" : modifié depuis la dernière synchronisation, doit être synchronisé à nouveau avec le serveur.
ContactsContract.RawContacts VERSION Numéro de version de cette ligne. Contacts Provider incrémente automatiquement cette valeur chaque fois que la ligne ou les modifications de données associées.
ContactsContract.Data DATA_VERSION Numéro de version de cette ligne. Contacts Provider incrémente automatiquement cette valeur chaque fois que la ligne de données est modifié.
ContactsContract.RawContacts SOURCE_ID Valeur de chaîne qui identifie de manière unique ce contact brut dans le compte dans dans lequel il a été créé. Lorsqu'un adaptateur de synchronisation crée un contact brut, cette colonne doit être définie sur la l'identifiant unique du serveur pour le contact brut. Lorsqu'une application Android crée un contact brut, elle doit laisser cette colonne vide. Cela indique que la synchronisation qu'il doit créer un nouveau contact brut sur le serveur et obtenir pour SOURCE_ID.

Plus précisément, l'ID de la source doit être unique pour chaque compte. et doit être stable entre les synchronisations:

  • Unique : chaque contact brut d'un compte doit avoir son propre ID de source. Si vous ne l'appliquez pas, vous causerez des problèmes dans l'application Contacts. Notez que deux contacts bruts pour le même type de compte peuvent avoir le même ID de source. Par exemple, le contact brut "Thomas Higginson" du compte emily.dickinson@gmail.com peut avoir le même ID de source que le contact brut "Thomas Higginson" du compte emilyd@gmail.com.
  • Stable : les ID de source font partie intégrante des données du service en ligne pour le contact brut. Par exemple, si l'utilisateur efface le stockage des contacts Paramètres de l'application et resynchronisations, les contacts bruts restaurés doivent avoir le même comme précédemment. Si vous ne le faites pas, les raccourcis ne fonctionneront plus.
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 n'appartenant à aucun groupe sont invisibles pour les interfaces d'application Android. Par défaut, les contacts sont invisibles si aucun de leurs contacts bruts n'appartient à un groupe. (L'appartenance à un groupe d'un contact brut est indiquée par un ou plusieurs ContactsContract.CommonDataKinds.GroupMembership lignes dans la table ContactsContract.Data). En définissant cet indicateur dans la ligne ContactsContract.Settings du tableau pour un type de compte et un compte donnés, vous pouvez forcer la visibilité des contacts sans groupe. Cet indicateur permet, par exemple, d'afficher les contacts des serveurs qui n'utilisent pas de groupes.
"1" : pour ce compte et ce type de compte, les contacts qui n'appartiennent pas à un groupe sont visibles par les UI de l'application.
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 lot.
  • Récupération et modification avec des intents
  • Intégrité des données

La procédure de modification d'un adaptateur de synchronisation est également expliquée plus en détail dans la section Adaptateurs de synchronisation Contacts Provider.

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 d'une personne, vous pouvez récupérer ContactsContract.RawContacts lignes pour un seul ContactsContract.Contacts, ou toutes les ContactsContract.CommonDataKinds.Email lignes pour un seul Ligne ContactsContract.RawContacts. Pour faciliter cela, le fournisseur de contacts propose des constructions entité, qui agissent comme des jointures de base 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 basés sur les colonnes disponibles auprès de l'entité. Le résultat est un Cursor contenant une ligne pour chaque ligne de la table enfant qui a été récupérée. Par exemple, si vous lancez une requête ContactsContract.Contacts.Entity pour le nom d'un contact et toutes les lignes ContactsContract.CommonDataKinds.Email pour toutes les contacts bruts pour ce nom, vous obtenez une 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. Par ailleurs, le fournisseur de contacts traite une requête sur une entité en une seule transaction, ce qui garantit que les données récupérées sont cohérents en interne.

Remarque:Une entité ne contient généralement pas toutes les colonnes des colonnes table enfant. Si vous essayez de travailler avec un nom de colonne qui ne figure pas dans la liste des noms de colonne constantes pour l'entité, vous obtenez une 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 "détails". L'activité principale affiche une liste de lignes de contact ; Lorsque l'utilisateur en sélectionne un, l'activité envoie son ID au détail activité. L'activité "Détail" utilise le ContactsContract.Contacts.Entity pour afficher toutes les lignes de données de tous les contacts bruts associés à l'élément sélectionné contact.

Cet extrait est issu de la ligne "detail" activité:

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 invoque un rappel pour 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 Contacts Provider dans "mode par lot" en créant une ArrayList Objets ContentProviderOperation et appels applyBatch() En effet, le fournisseur de contacts effectue toutes les opérations applyBatch() en un seul transaction, vos modifications ne laisseront jamais le référentiel de contacts dans un de l'état. Une modification par lot facilite également l'insertion d'un contact brut et de ses données détaillées en même temps.

Remarque:Pour modifier un seul contact brut, envisagez d'envoyer un intent à l'application de gestion des 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 nuit à l'expérience utilisateur globale. Pour organiser toutes les modifications que vous souhaitez dans le moins de listes distinctes possible, tout en les empêchant bloquant le système, définissez 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 d'autres processus s'exécuter et ferme 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 donnent lieu à plus d'une transaction par appel applyBatch() En raison de 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 une lignes de contact brutes et les lignes de données associées, ou la dernière opération pour 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éussir ou échouer comme une seule unité. Si vous ne définissez aucun point de rendement, la plus petite opération atomique est l'ensemble du lot d'opérations. Si vous utilisez des points de rendement, des opérations de dégradation des performances du système, tout en garantissant qu'un sous-ensemble est atomique.

Références de modification d'arrière

Lorsque vous insérez une nouvelle ligne de contact brut et ses lignes de données associées en tant qu'ensemble d'objets ContentProviderOperation, vous devez associer les lignes de données à la ligne de contact brut en insérant la valeur _ID du contact brut en tant que valeur RAW_CONTACT_ID. Toutefois, La valeur n'est pas disponible lorsque vous créez le ContentProviderOperation pour la ligne de données, car vous n'avez pas encore appliqué 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 à l'aide de la méthode le résultat d'une opération précédente.

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 de applyBatch(). En tant que les opérations par lot sont appliquées, le résultat de chaque opération est stocké dans un tableau intermédiaire de résultats. La valeur previousResult correspond à l'index de l'un de ces résultats, qui est récupéré et stocké avec le key . 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 ligne ContactsContract.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 objets ContentProviderOperation que vous fournissez. Cependant, les éléments du tableau de résultats sont définis sur null, et si vous essayez pour 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 erreur Exception.

Les extraits suivants montrent comment insérer un contact brut et des données 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 le nom à afficher, le numéro de téléphone et l'adresse e-mail.

Chaque objet de compilateur d'opération utilise withValueBackReference() pour obtenir 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. Chaque ligne de données est donc automatiquement associée RAW_CONTACT_ID à la nouvelle ligne ContactsContract.RawContacts à laquelle il 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 permettent également d'implémenter un contrôle de simultanéité optimiste, une méthode permettant d'appliquer des transactions de modification sans avoir à verrouiller le dépôt sous-jacent. Pour utiliser cette méthode, appliquez la transaction, puis recherchez d'autres modifications peuvent avoir été effectué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 la concurrence optimiste est utile pour un appareil mobile, où 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'un seul ContactsContract.RawContacts, procédez comme suit:

  1. Récupérez la colonne VERSION du contact brut, ainsi que les autres données que vous récupérez.
  2. Créez un objet ContentProviderOperation.Builder adapté à appliquer une contrainte, à l'aide de la méthode newAssertQuery(Uri) Pour l'URI de contenu, utiliser RawContacts.CONTENT_URI auquel est ajouté le _ID du contact brut.
  3. Pour l'objet ContentProviderOperation.Builder, appelez withValue() pour comparer les VERSION par le numéro de version que vous venez de récupérer.
  4. Pour le même ContentProviderOperation.Builder, appelez withExpectedCount() pour garantir qu'une seule ligne est testée par cette assertion.
  5. Appelez build() pour créer l'objet ContentProviderOperation, puis ajoutez cet objet en tant que premier objet dans le ArrayList que vous transmettez à applyBatch().
  6. 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 pourrez alors choisir de réessayer ou effectuer une autre action.

L'extrait de code suivant montre comment créer une déclaration ContentProviderOperation après avoir interrogé un seul contact brut à l'aide de un CursorLoader:

Kotlin

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID))
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION))
}

...

// Sets up a Uri for the assert operation
val rawContactUri: Uri = ContentUris.withAppendedId(
        ContactsContract.RawContacts.CONTENT_URI,
        rawContactID
)

// Creates a builder for the assert operation
val assertOp: ContentProviderOperation.Builder =
        ContentProviderOperation.newAssertQuery(rawContactUri).apply {
            // Adds the assertions to the assert operation: checks the version
            withValue(SyncColumns.VERSION, mVersion)

            // and count of rows tested
            withExpectedCount(1)
        }

// Creates an ArrayList to hold the ContentProviderOperation objects
val ops = arrayListOf<ContentProviderOperation>()

ops.add(assertOp.build())

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try {
    val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops)
} catch (e: OperationApplicationException) {
    // Actions you want to take if the assert operation fails go here
}

Java

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}

...

// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(rawContactUri);

// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);

// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperation>;

ops.add(assertOp.build());

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try
    {
        ContentProviderResult[] results =
                getContentResolver().applyBatch(AUTHORITY, ops);

    } catch (OperationApplicationException e) {

        // Actions you want to take if the assert operation fails go here
    }

Récupération et modification avec des intents

L'envoi d'un intent à l'application de gestion des contacts de l'appareil vous permet d'accéder à la liste Fournisseur indirectement. L'intent lance l'interface utilisateur de l'application Contacts de l'appareil, dans laquelle les utilisateurs peuvent faire du travail lié 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 ajoute ou met à jour des données, vous pouvez d'abord les collecter et les envoyer en tant que 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 une autorisation en lecture ou en écriture au fournisseur. L'application Contacts de l'appareil peut l'autorisation de lecture pour un contact et, comme vous apportez des modifications au via une autre application, vous n'avez pas besoin d'autorisations en écriture.

Le processus général d'envoi d'un intent pour accéder à un fournisseur est décrit en détail dans le guide Principes de base 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 :

Tableau 4. Intents du fournisseur de contacts

Tâche Action Données Type MIME Notes
Sélectionner un contact dans une liste ACTION_PICK Au choix: <ph type="x-smartling-placeholder"> Non utilisé Affiche une liste de contacts bruts ou une liste de données provenant d'un contact brut, en fonction de la que vous fournissez.

Appeler startActivityForResult(), qui renvoie l'URI de contenu de la ligne sélectionnée. Le format de l'URI correspond à l'URI de contenu de la table, auquel est ajouté le LOOKUP_ID de la ligne. L'application de gestion des contacts de l'appareil délègue les autorisations de lecture et d'écriture à cet URI de contenu pour le cycle de vie de votre activité. Consultez le <ph type="x-smartling-placeholder"></ph> Principes de base des fournisseurs de contenu.

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. La les valeurs supplémentaires que vous ajoutez à l'intent s'affichent. Si envoyé avec startActivityForResult(), l'URI de contenu du contact brut nouvellement ajouté est renvoyé à l'API onActivityResult() de rappel dans l'argument Intent, dans "données" . Pour obtenir la valeur, appelez getData().
Modifier un contact ACTION_EDIT CONTENT_LOOKUP_URI pour le contact. L'activité d'édition permettra à l'utilisateur de modifier les données associées avec ce contact. Contacts.CONTENT_ITEM_TYPE, un seul contact. Affiche l'écran Modifier le contact dans l'application Contacts. Les valeurs supplémentaires que vous ajoutez à l'intent sont affichées. Lorsque l'utilisateur clique sur OK pour enregistrer les modifications, votre activité revient au premier plan.
Affichez un sélecteur permettant d'ajouter des données. ACTION_INSERT_OR_EDIT N/A CONTENT_ITEM_TYPE Cet intent affiche toujours l'écran de sélection de l'application Contacts. L'utilisateur peut : sélectionnez un contact à modifier ou ajoutez-en un. L'écran de modification ou d'ajout s'affiche, selon le 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 les données à un contact existant. contact,

Remarque:Il n'est pas nécessaire d'envoyer une valeur "name" dans les extras de cet intent. car l'utilisateur choisit toujours un nom existant ou en ajoute un nouveau. De plus, si vous envoyez un nom et que l'utilisateur choisit de le modifier, l'application Contacts affiche le nom que vous envoyez, en écrasant la valeur précédente. Si l'utilisateur n'a pas notez que la modification est enregistrée et 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

Parce que le référentiel de contacts contient des données importantes et sensibles que les utilisateurs s'attendent à voir correctes et à jour, le fournisseur de contacts dispose de règles bien définies pour l'intégrité des données. Il est de votre responsabilité de vous conformer à ces règles lorsque vous modifiez les données de contact. Voici les règles importantes :

Ajoutez toujours une ligne ContactsContract.CommonDataKinds.StructuredName pour chaque ligne ContactsContract.RawContacts que vous ajoutez.
Une ligne ContactsContract.RawContacts sans ContactsContract.CommonDataKinds.StructuredName ligne dans La table ContactsContract.Data peut causer des problèmes pendant l'agrégation.
Associez toujours les nouvelles lignes ContactsContract.Data à leur ligne ContactsContract.RawContacts parente.
Une ligne ContactsContract.Data qui n'est pas associée à un ContactsContract.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 ni ne supprime que les données des lignes qui vous appartiennent, et qu'elle n'insère que des données avec un type et un nom de compte 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 valeurs TYPE.
L'utilisation de ces constantes vous permet d'éviter les erreurs. Vous recevrez aussi une notification avec le 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 s'affichent, mais ne peuvent être ni modifiées, ni supprimées, et les utilisateurs ne peuvent pas ajouter des 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'élément <ContactsAccountType> et un ou plusieurs de ses <ContactsDataKind> éléments enfants. Ce point est décrit plus en détail dans la section <ContactsDataKind> element.

Pour en savoir plus sur les types MIME personnalisés, consultez le <ph type="x-smartling-placeholder"></ph> Créer un guide pour les fournisseurs de contenu

Adaptateurs de synchronisation Contacts Provider

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 des données existantes dans 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 facilite les données des contacts disponibles même lorsque l'appareil n'est pas connecté au réseau.

Bien que vous puissiez implémenter la synchronisation de différentes manières, le système Android fournit un framework de synchronisation de plug-ins qui automatise les tâches suivantes :

  • Vérification de la disponibilité du réseau.
  • Planification et exécution de la synchronisation en fonction des préférences de l'utilisateur
  • Redémarrer les synchronisations qui ont été arrêtées.

Pour utiliser ce framework, vous devez fournir un plug-in d'adaptateur de synchronisation. Chaque adaptateur de synchronisation est propre à un service et à un fournisseur de contenu, mais 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 des adaptateurs

Vous implémentez un adaptateur de synchronisation en tant que sous-classe de AbstractThreadedSyncAdapter et l'installer dans un environnement Android application. Le système apprend l'existence de l'adaptateur de synchronisation à partir des éléments de votre application le fichier manifeste, et à partir d'un fichier XML spécial versé par le fichier manifeste. Le fichier XML définit le type de compte pour le service en ligne et l'autorité pour le fournisseur de contenu, qui, ensemble, identifier l'adaptateur de manière unique. L'adaptateur de synchronisation ne devient actif que lorsque l'utilisateur ajoute un prend en compte le type de compte de l'adaptateur de synchronisation et active la synchronisation du contenu fournisseur 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 le système doit détecter et regrouper les adaptateurs de synchronisation qui accèdent à différents services même organisation. Par exemple, les adaptateurs de synchronisation des services en ligne de 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 lors des étapes suivantes:

  1. Collecte le nom, le mot de passe ou des informations similaires de l'utilisateur (ses identifiants).
  2. Envoie les identifiants au service
  3. Il 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 des plug-ins, AccountManager peut fournir l'accès à n'importe quel jeton d'authentification d'un authentificateur prend en charge et choisit d'exposer, comme les jetons d'authentification OAuth2.

Bien que l'authentification ne soit pas requise, la plupart des services de gestion des contacts l'utilisent. Toutefois, vous n'êtes pas obligé d'utiliser le framework d'authentification Android pour vous authentifier.

Implémentation de l'adaptateur de synchronisation

Pour implémenter un adaptateur de synchronisation pour le fournisseur de contacts, commencez par créer une application Android contenant les éléments suivants :

Un composant Service qui répond aux requêtes du système pour s'associer à l'adaptateur de synchronisation.
Lorsque le système souhaite exécuter une synchronisation, il appelle la méthode onBind() du service pour obtenir un IBinder pour l'adaptateur de synchronisation. Cela permet au système d'effectuer les appels inter-processus aux méthodes de l'adaptateur.
L'adaptateur de synchronisation réel, implémenté comme une sous-classe concrète de AbstractThreadedSyncAdapter
Cette classe se charge de télécharger les données à partir du serveur et d'importer les données à partir du et résoudre les conflits. Le travail principal de l'adaptateur est effectué dans la méthode onPerformSync(). Cette classe doit être instanciée en tant que singleton.
Une sous-classe de Application.
Cette classe sert de fabrique pour le singleton de l'adaptateur de synchronisation. Utilisez les onCreate() pour instancier l'adaptateur de synchronisation. fournissent un "getter" statique pour renvoyer le singleton à la onBind() de l'adaptateur de synchronisation Google Cloud.
Facultatif : composant Service qui répond aux requêtes du système pour l'authentification des utilisateurs.
AccountManager démarre ce service pour commencer l'authentification processus. La méthode onCreate() du service instancie un objet authentificateur. Lorsque le système veut authentifier un compte d'utilisateur pour l'adaptateur de synchronisation de l'application, celui-ci appelle onBind() pour obtenir une IBinder 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 pour l'authentification unique.
Cette classe fournit des méthodes que AccountManager appelle pour authentifier les identifiants de l'utilisateur auprès du serveur. Les détails processus d'authentification varient considérablement, en fonction de la technologie de serveur utilisée. Vous devez 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 du service d'adaptateur de synchronisation et du service d'authentification décrits précédemment défini dans <service> dans le 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 XML res/xml/syncadapter.xml. À son tour, ce fichier spécifie un URI pour le service Web qui sera synchronisé avec Contacts Provider et un type de compte pour le service Web.
  • Facultatif : L'élément <meta-data> de l'authentificateur pointe vers le fichier XML res/xml/authenticator.xml. Ce fichier spécifie quant à lui compatible avec cet authentificateur, ainsi que les ressources d'UI lors du processus d'authentification. Le type de compte spécifié dans ce doit être identique au type de compte spécifié pour la synchronisation. adaptateur secteur.

Données de flux sur les 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 à partir de votre propre réseau à ces tables, ou vous pouvez lire des données de flux à partir de ces tables et les afficher dans votre propre application, ou les deux. Grâce à ces fonctionnalités, vos réseaux sociaux les services et applications peuvent être intégrés dans l'expérience de réseau social d'Android.

Texte du flux sur les réseaux sociaux

Les éléments de flux sont toujours associés à un contact brut. La android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID renvoie vers le Valeur _ID pour le 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é à l'élément de flux. N'oubliez pas de définir cette valeur lorsque vous insérez un élément de flux.
Colonnes d'identifiants
Obligatoire. Vous devez insérer les colonnes d'identifiants suivantes lorsque vous insérer un élément de flux: <ph type="x-smartling-placeholder">
    </ph>
  • 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 : android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY de la valeur contact auquel cet élément de flux est associé.
  • android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID : Valeur android.provider.BaseColumns#_ID du contact brut que ce flux auquel l'élément 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 (contenu publié par la source de l'élément) ou la 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 ellipsize contenu long, mais essaiera d’éviter de casser les balises.
android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP
Chaîne de texte contenant l'heure à laquelle l'élément de flux a été inséré ou mis à jour, au format de millisecondes depuis l'epoch. Les applications qui insèrent ou mettent à jour des éléments de flux sont responsable de la gestion de cette colonne ; elle n'est pas automatiquement gérée Fournisseur de contacts.

Pour afficher les informations d'identification de vos éléments de flux, utilisez l'événement android.provider.ContactsContract.StreamItemsColumns#RES_ICON, android.provider.ContactsContract.StreamItemsColumns#RES_LABEL et android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE pour créer un lien vers 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, réservées à l'usage exclusif des adaptateurs de synchronisation.

Photos du flux sur les réseaux sociaux

La table android.provider.ContactsContract.StreamItemPhotos stocke les photos associées avec 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 tableau dans ces colonnes:

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 de Contacts. Fournisseur qui l'a utilisé 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 est disponible à la lecture.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID
Identifiant numérique d'une photo pour un contact brut. Ajoutez cette valeur à la constante DisplayPhoto.CONTENT_URI pour obtenir un URI de contenu pointant vers un seul fichier photo, puis appelez openAssetFileDescriptor() pour obtenir un gestionnaire du fichier photo.
android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI
URI de contenu pointant directement vers le fichier photo de la photo représentée par cette ligne. Appelez openAssetFileDescriptor() avec cet URI pour obtenir un handle vers le fichier photo.

Utiliser les tableaux des flux de 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 fait de la place pour de nouvelles lignes d'éléments de flux en supprimant automatiquement les lignes comportant la plus ancienne 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éfinis sur null. La requête renvoie un Cursor 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-tableau d'android.provider.ContactsContract.StreamItemPhotos contenant la photo pour un même élément de flux.

Interactions avec les flux sur les réseaux sociaux

Les données de flux de réseaux sociaux gérées par le fournisseur de contacts, conjointement avec le l'application Contacts de votre appareil, offre un moyen efficace de connecter votre système de réseau social avec vos contacts existants. Les fonctionnalités suivantes sont disponibles:

  • En synchronisant votre service de réseau social avec le fournisseur de contacts à l'aide d'une fonction de synchronisation adaptateur, vous pouvez récupérer l'activité récente des contacts d'un utilisateur et la stocker dans 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 dans l'application Contacts de l'appareil et dans l'application Contacts vous pouvez recevoir un intent lorsqu'un contact est consulté, et à ce stade mettre à jour l'état du contact à 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 tout en le consultant dans l'application Contacts de l'appareil. Vous activez cette option avec l'option "Inviter un contact" fonctionnalité, que vous activez en combinant une activité qui ajoute un contact existant à votre réseau, et un fichier XML qui fournit l'application de contacts de l'appareil et le Contact Provider avec les détails de votre application.

La synchronisation régulière des éléments de flux avec Contacts Provider s'effectue de la même manière que ou effectuer d'autres synchronisations. Pour en savoir plus sur la synchronisation, consultez la section Adaptateurs de synchronisation du fournisseur de contacts. L'enregistrement des notifications et inviter des contacts sont abordés dans les deux sections suivantes.

S'inscrire pour gérer les vues de réseaux sociaux

Pour enregistrer votre adaptateur de synchronisation afin de recevoir des notifications lorsque l'utilisateur consulte un contact géré par votre adaptateur de synchronisation:

  1. Créez un fichier nommé contacts.xml dans le répertoire res/xml/ de votre projet. Si vous disposez déjà de ce fichier, vous pouvez ignorer cette étape.
  2. 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.
  3. 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 étend IntentService 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 de notification, vous pouvez vous associer à votre adaptateur de synchronisation, puis l'appeler pour mettre à jour les données du contact brut.

Pour enregistrer une activité à appeler lorsque l'utilisateur clique sur un élément de flux, une photo ou les deux:

  1. Créez un fichier nommé contacts.xml dans le répertoire res/xml/ de votre projet. Si vous disposez déjà de ce fichier, vous pouvez ignorer cette étape.
  2. 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.
  3. Pour enregistrer l'une de vos activités afin de gérer le clic de l'utilisateur sur un élément de flux dans l'application Contacts de l'appareil, ajoutez l'attribut viewStreamItemActivity="activityclass" à l'élément, où activityclass est le nom de classe complet de l'activité qui doit recevoir l'intent de l'application Contacts de l'appareil.
  4. Pour enregistrer l'une de vos activités afin de gérer les clics de l'utilisateur sur une photo de flux dans la 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 de 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 lesquels l'utilisateur a cliqué. Pour avoir des activités distinctes pour les articles textuels et les photos, utilisez les deux attributs dans le même fichier.

Interagir avec votre service de réseautage social

Les utilisateurs n'ont pas besoin de quitter l'application Contacts de l'appareil pour inviter un contact à rejoindre votre site de réseautage 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 :

  1. Créez un fichier nommé contacts.xml dans le répertoire res/xml/ de votre projet. Si vous disposez déjà de ce fichier, vous pouvez ignorer cette étape.
  2. 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.
  3. Ajoutez les attributs suivants :
    • inviteContactActivity="activityclass"
    • inviteContactActionLabel="@string/invite_action_label"
    La valeur activityclass correspond au nom de classe complet de l'activité qui doit recevoir l'intent. invite_action_label est une chaîne de texte qui s'affiche dans le menu Ajouter une connexion dans la l'application Contacts de votre 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 avec l'application Contacts. Elle 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 affichée pour l'activité spécifiée dans inviteContactActivity, dans le menu Ajouter une connexion. Par exemple, vous pouvez utiliser la chaîne "Suivre dans mon réseau". Vous pouvez utiliser un identifiant de ressource de chaîne pour ce libellé.
viewContactNotifyService
Nom de classe complet d'un service de votre application 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. Cette fonctionnalité est décrite plus en détail dans la section Interactions avec les flux sur les réseaux sociaux :
viewGroupActivity
Nom de classe complet d'une activité de votre application pouvant afficher des informations de groupe. Lorsque l'utilisateur clique sur le libellé de groupe dans l'application Contacts de l'appareil, l'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 des groupes dans votre application.

Un identifiant de ressource de chaîne est autorisé pour cet attribut.

viewStreamItemActivity
Nom de classe complet d'une activité dans votre application que l'appareil L'application de gestion des contacts se 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.

<ContactsDataKind (Genre de données de contacts) élément

L'élément <ContactsDataKind> contrôle l'affichage des des lignes de données personnalisées dans l'interface utilisateur 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 que l'application Contacts affiche le contenu d'une ligne de données personnalisée au format une partie des détails d'un contact brut. Chaque élément enfant <ContactsDataKind> de <ContactsAccountType> représente un type de ligne de données personnalisées que vous synchronisez l'adaptateur est ajouté à la table ContactsContract.Data. Ajoutez un élément <ContactsDataKind> pour chaque type MIME personnalisé que vous utilisez. Vous n'avez pas pour 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 valeur vnd.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 de la première des deux valeurs extraites de la ligne de données. La valeur s'affiche sur la première ligne de l'entrée de 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 s'affiche sur la deuxième ligne de l'entrée de cette ligne de données. Voir aussi android:summaryColumn

Fonctionnalités supplémentaires de Contacts Provider

En plus des fonctionnalités principales décrites dans les sections précédentes, le fournisseur de contacts propose fonctions utiles suivantes pour exploiter les données de contacts:

  • 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 d'utilisateur souhaite gérer les groupes, l'adaptateur de synchronisation du type de compte du compte doit transférer regroupe les données 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 d'un ou de plusieurs auquel le contact appartient sont stockés dans la table ContactsContract.Data, en utilisant le type MIME ContactsContract.CommonDataKinds.GroupMembership.

Si vous concevez un adaptateur de synchronisation qui ajoutera des données de contact brutes au fournisseur de contacts et que vous n'utilisez pas de groupes, vous devez indiquer au Fournisseur pour rendre vos données visibles. Dans le code exécuté lorsqu'un utilisateur ajoute un compte sur l'appareil, mettez à jour le 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, Contacts Provider rendre visibles les données de vos 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 ligne La colonne CONTACT_ID est associée à Colonne _ID du contact brut auquel il appartient. La classe ContactsContract.Contacts.Photo définit un sous-tableau de ContactsContract.Contacts contenant des informations sur la photo d'un contact photo principale, 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 contiennent des exemples de récupération des 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 de réseaux sociaux concernant une personne peuvent également inclure des photos. Celles-ci sont stockées dans android.provider.ContactsContract.StreamItemPhotos, décrite plus en détail détail dans la section Photos du flux de réseau social.