Récupérer les coordonnées d'un contact

Cette leçon explique comment récupérer les données détaillées d'un contact, telles que les adresses e-mail, les numéros de téléphone, etc. Ce sont les informations que les utilisateurs recherchent lorsqu'ils récupèrent un contact. Vous pouvez leur fournir tous les détails d'un contact ou n'afficher que les détails d'un certain type, comme les adresses e-mail.

Les étapes de cette leçon supposent que vous disposez déjà d'une ligne ContactsContract.Contacts pour un contact sélectionné par l'utilisateur. La leçon Récupérer les noms des contacts explique comment récupérer une liste de contacts.

Récupérer tous les détails d'un contact

Pour récupérer tous les détails d'un contact, recherchez dans la table ContactsContract.Data toutes les lignes contenant l'identifiant LOOKUP_KEY du contact. Cette colonne est disponible dans la table ContactsContract.Data, car le fournisseur de contacts effectue une jointure implicite entre la table ContactsContract.Contacts et la table ContactsContract.Data. La colonne LOOKUP_KEY est décrite plus en détail dans la leçon Récupérer des noms de contacts.

Remarque:La récupération de toutes les informations d'un contact réduit les performances de l'appareil, car celle-ci doit extraire toutes les colonnes de la table ContactsContract.Data. Avant d'utiliser cette technique, tenez compte de l'impact sur les performances.

Demander des autorisations

Pour lire les données à partir du fournisseur de contacts, votre application doit disposer de l'autorisation READ_CONTACTS. Pour demander cette autorisation, ajoutez l'élément enfant suivant de <manifest> à votre fichier manifeste:

    <uses-permission android:name="android.permission.READ_CONTACTS" />

Configurer une projection

Selon le type de données d'une ligne, celle-ci peut n'utiliser que quelques colonnes ou plusieurs. De plus, les données se trouvent dans différentes colonnes en fonction du type de données. Pour vous assurer d'obtenir toutes les colonnes possibles pour tous les types de données possibles, vous devez ajouter tous les noms de colonnes à votre projection. Récupérez toujours Data._ID si vous liez le résultat Cursor à un ListView. Sinon, la liaison ne fonctionnera pas. Récupérez également Data.MIMETYPE afin de pouvoir identifier le type de données de chaque ligne récupérée. Par exemple :

Kotlin

private val PROJECTION: Array<out String> = arrayOf(
        ContactsContract.Data._ID,
        ContactsContract.Data.MIMETYPE,
        ContactsContract.Data.DATA1,
        ContactsContract.Data.DATA2,
        ContactsContract.Data.DATA3,
        ContactsContract.Data.DATA4,
        ContactsContract.Data.DATA5,
        ContactsContract.Data.DATA6,
        ContactsContract.Data.DATA7,
        ContactsContract.Data.DATA8,
        ContactsContract.Data.DATA9,
        ContactsContract.Data.DATA10,
        ContactsContract.Data.DATA11,
        ContactsContract.Data.DATA12,
        ContactsContract.Data.DATA13,
        ContactsContract.Data.DATA14,
        ContactsContract.Data.DATA15
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.Data._ID,
                ContactsContract.Data.MIMETYPE,
                ContactsContract.Data.DATA1,
                ContactsContract.Data.DATA2,
                ContactsContract.Data.DATA3,
                ContactsContract.Data.DATA4,
                ContactsContract.Data.DATA5,
                ContactsContract.Data.DATA6,
                ContactsContract.Data.DATA7,
                ContactsContract.Data.DATA8,
                ContactsContract.Data.DATA9,
                ContactsContract.Data.DATA10,
                ContactsContract.Data.DATA11,
                ContactsContract.Data.DATA12,
                ContactsContract.Data.DATA13,
                ContactsContract.Data.DATA14,
                ContactsContract.Data.DATA15
            };

Cette projection récupère toutes les colonnes d'une ligne de la table ContactsContract.Data à l'aide des noms de colonnes définis dans la classe ContactsContract.Data.

Vous pouvez également utiliser d'autres constantes de colonne définies dans la classe ContactsContract.Data ou héritées par elle. Notez toutefois que les colonnes SYNC1 à SYNC4 sont destinées à être utilisées par des adaptateurs de synchronisation. Leurs données ne sont donc pas utiles.

Définir les critères de sélection

Définissez une constante pour votre clause de sélection, un tableau pour contenir les arguments de sélection et une variable pour contenir la valeur de sélection. Utilisez la colonne Contacts.LOOKUP_KEY pour trouver le contact. Par exemple :

Kotlin

// Defines the selection clause
private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")
/*
 * Defines a variable to contain the selection value. Once you
 * have the Cursor from the Contacts table, and you've selected
 * the desired row, move the row's LOOKUP_KEY value into this
 * variable.
 */
private var lookupKey: String? = null

Java

    // Defines the selection clause
    private static final String SELECTION = Data.LOOKUP_KEY + " = ?";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };
    /*
     * Defines a variable to contain the selection value. Once you
     * have the Cursor from the Contacts table, and you've selected
     * the desired row, move the row's LOOKUP_KEY value into this
     * variable.
     */
    private lateinit var lookupKey: String

L'utilisation de "?" comme espace réservé dans votre expression de texte de sélection garantit que la recherche obtenue est générée par liaison plutôt que par compilation SQL. Cette approche élimine le risque d'injection SQL malveillante.

Définir l'ordre de tri

Définissez l'ordre de tri souhaité dans le Cursor obtenu. Pour conserver toutes les lignes d'un type de données particulier, triez par Data.MIMETYPE. Cet argument de requête regroupe toutes les lignes d'e-mail, toutes les lignes de téléphone, etc. Par exemple :

Kotlin

/*
 * Defines a string that specifies a sort order of MIME type
 */
private const val SORT_ORDER = ContactsContract.Data.MIMETYPE

Java

    /*
     * Defines a string that specifies a sort order of MIME type
     */
    private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;

Remarque:Certains types de données n'utilisent pas de sous-type. Vous ne pouvez donc pas effectuer de tri par sous-type. Au lieu de cela, vous devez itérer l'élément Cursor renvoyé, déterminer le type de données de la ligne actuelle et stocker les données des lignes qui utilisent un sous-type. Une fois que vous avez fini de lire le curseur, vous pouvez trier chaque type de données par sous-type et afficher les résultats.

Initialiser le chargeur

Effectuez toujours les récupérations auprès du fournisseur de contacts (et de tous les autres fournisseurs de contenu) dans un thread d'arrière-plan. Utilisez le framework Loader défini par la classe LoaderManager et l'interface LoaderManager.LoaderCallbacks pour effectuer des récupérations en arrière-plan.

Lorsque vous êtes prêt à récupérer les lignes, initialisez le framework du chargeur en appelant initLoader(). Transmettez un identifiant de type entier à la méthode. Cet identifiant est transmis aux méthodes LoaderManager.LoaderCallbacks. Cet identifiant vous permet d'utiliser plusieurs chargeurs dans une application en les différenciant.

L'extrait de code suivant montre comment initialiser le framework Loader:

Kotlin

// Defines a constant that identifies the loader
private const val DETAILS_QUERY_ID: Int = 0

class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Initializes the loader framework
        loaderManager.initLoader(DETAILS_QUERY_ID, null, this)

Java

public class DetailsFragment extends Fragment implements
        LoaderManager.LoaderCallbacks<Cursor> {
    ...
    // Defines a constant that identifies the loader
    static int DETAILS_QUERY_ID = 0;
    ...
    @Override
    public void onCreate(Bundle savedInstanceState) {
        ...
        // Initializes the loader framework
        getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);

Implémenter onCreateLoader()

Implémentez la méthode onCreateLoader(), qui est appelée par le framework du chargeur immédiatement après l'appel de initLoader(). Renvoyez un CursorLoader à partir de cette méthode. Comme vous effectuez une recherche dans la table ContactsContract.Data, utilisez la constante Data.CONTENT_URI comme URI de contenu. Par exemple :

Kotlin

override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
    // Choose the proper action
    mLoader = when(loaderId) {
        DETAILS_QUERY_ID -> {
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey
            // Starts the query
            activity?.let {
                CursorLoader(
                        it,
                        ContactsContract.Data.CONTENT_URI,
                        PROJECTION,
                        SELECTION,
                        selectionArgs,
                        SORT_ORDER
                )
            }
        }
        ...
    }
    return mLoader
}

Java

@Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        // Choose the proper action
        switch (loaderId) {
            case DETAILS_QUERY_ID:
            // Assigns the selection parameter
            selectionArgs[0] = lookupKey;
            // Starts the query
            CursorLoader mLoader =
                    new CursorLoader(
                            getActivity(),
                            ContactsContract.Data.CONTENT_URI,
                            PROJECTION,
                            SELECTION,
                            selectionArgs,
                            SORT_ORDER
                    );
    }

Implémenter onLoadFinished() et onLoaderReset()

Implémentez la méthode onLoadFinished(). Le framework du chargeur appelle onLoadFinished() lorsque le fournisseur de contacts renvoie les résultats de la requête. Par exemple :

Kotlin

    override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) {
        when(loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * Process the resulting Cursor here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                    /*
                     * Process the resulting Cursor here.
                     */
                }
                break;
            ...
        }
    }

La méthode onLoaderReset() est appelée lorsque le framework du chargeur détecte que les données qui sauvegardent le résultat Cursor ont été modifiées. À ce stade, supprimez toutes les références existantes à Cursor en les définissant sur "null". Si vous ne le faites pas, le framework du chargeur ne détruira pas l'ancien Cursor, et vous obtiendrez une fuite de mémoire. Par exemple :

Kotlin

    override fun onLoaderReset(loader: Loader<Cursor>) {
        when (loader.id) {
            DETAILS_QUERY_ID -> {
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
            }
            ...
        }
    }

Java

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        switch (loader.getId()) {
            case DETAILS_QUERY_ID:
                /*
                 * If you have current references to the Cursor,
                 * remove them here.
                 */
                }
                break;
    }

Récupérer les informations spécifiques d'un contact

La récupération d'un type de données spécifique pour un contact, par exemple tous les e-mails, suit le même schéma que la récupération de tous les détails. Voici les seules modifications que vous devez apporter au code indiqué dans la section Récupérer tous les détails d'un contact:

Prévision
Modifiez votre projection pour récupérer les colonnes spécifiques au type de données. Modifiez également la projection pour utiliser les constantes de nom de colonne définies dans la sous-classe ContactsContract.CommonDataKinds correspondant au type de données.
Sélection
Modifiez le texte de sélection pour rechercher la valeur MIMETYPE spécifique à votre type de données.
Ordre de tri
Étant donné que vous ne sélectionnez qu'un seul type de détail, ne regroupez pas les Cursor renvoyés par Data.MIMETYPE.

Ces modifications sont décrites dans les sections suivantes.

Définir une projection

Définissez les colonnes que vous souhaitez récupérer en utilisant les constantes de nom de colonne dans la sous-classe de ContactsContract.CommonDataKinds pour le type de données. Si vous prévoyez d'associer votre Cursor à un ListView, veillez à récupérer la colonne _ID. Par exemple, pour récupérer des données de messagerie, définissez la projection suivante:

Kotlin

private val PROJECTION: Array<String> = arrayOf(
        ContactsContract.CommonDataKinds.Email._ID,
        ContactsContract.CommonDataKinds.Email.ADDRESS,
        ContactsContract.CommonDataKinds.Email.TYPE,
        ContactsContract.CommonDataKinds.Email.LABEL
)

Java

    private static final String[] PROJECTION =
            {
                ContactsContract.CommonDataKinds.Email._ID,
                ContactsContract.CommonDataKinds.Email.ADDRESS,
                ContactsContract.CommonDataKinds.Email.TYPE,
                ContactsContract.CommonDataKinds.Email.LABEL
            };

Notez que cette projection utilise les noms de colonnes définis dans la classe ContactsContract.CommonDataKinds.Email, au lieu de ceux définis dans la classe ContactsContract.Data. L'utilisation de noms de colonnes spécifiques aux adresses e-mail améliore la lisibilité du code.

Dans la projection, vous pouvez également utiliser n'importe quelle autre colonne définie dans la sous-classe ContactsContract.CommonDataKinds.

Définir les critères de sélection

Définissez une expression de texte de recherche qui récupère les lignes correspondant au LOOKUP_KEY d'un contact spécifique et au Data.MIMETYPE des détails de votre choix. Placez la valeur MIMETYPE entre guillemets simples en concaténant un caractère "'" (guillemet simple) au début et à la fin de la constante. Sinon, le fournisseur interprète la constante comme un nom de variable plutôt que comme une valeur de chaîne. Vous n'avez pas besoin d'utiliser d'espace réservé pour cette valeur, car vous utilisez une constante plutôt qu'une valeur fournie par l'utilisateur. Par exemple :

Kotlin

/*
 * Defines the selection clause. Search for a lookup key
 * and the Email MIME type
 */
private const val SELECTION =
        "${ContactsContract.Data.LOOKUP_KEY} = ? AND " +
        "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'"
...
// Defines the array to hold the search criteria
private val selectionArgs: Array<String> = arrayOf("")

Java

    /*
     * Defines the selection clause. Search for a lookup key
     * and the Email MIME type
     */
    private static final String SELECTION =
            Data.LOOKUP_KEY + " = ?" +
            " AND " +
            Data.MIMETYPE + " = " +
            "'" + Email.CONTENT_ITEM_TYPE + "'";
    // Defines the array to hold the search criteria
    private String[] selectionArgs = { "" };

Définir un ordre de tri

Définissez un ordre de tri pour le Cursor renvoyé. Étant donné que vous récupérez un type de données spécifique, omettez le tri sur MIMETYPE. Si le type de données détaillées que vous recherchez inclut un sous-type, effectuez un tri en fonction de celui-ci. Par exemple, pour les données de messagerie, vous pouvez trier en fonction de Email.TYPE:

Kotlin

private const val SORT_ORDER: String = "${Email.TYPE} ASC"

Java

    private static final String SORT_ORDER = Email.TYPE + " ASC ";