Contacts du profil professionnel

Ce guide du développeur explique comment améliorer votre application afin d'utiliser les données de contact du profil professionnel. Si vous n'avez jamais utilisé les API de gestion des contacts d'Android, consultez Fournisseur de contacts pour vous familiariser avec les API.

Présentation

Les appareils dotés de profils professionnels stockent les contacts dans des répertoires locaux distincts pour les profils professionnel et personnel. Par défaut, lorsqu'une application s'exécute dans le profil personnel, elle n'affiche pas les contacts professionnels. Toutefois, une application peut accéder aux coordonnées à partir du profil professionnel. Par exemple, l'application Android Contacts de Google affiche à la fois les contacts personnels et les contacts de l'annuaire professionnel dans les résultats de recherche.

Les utilisateurs souhaitent souvent utiliser leurs applications et appareils personnels pour le travail. En utilisant les contacts du profil professionnel, votre application peut faire partie de la journée de travail de l'utilisateur.

Expérience utilisateur

Réfléchissez à la manière dont votre application peut présenter les coordonnées issues du profil professionnel. La meilleure approche dépend de la nature de votre application et de la raison pour laquelle les utilisateurs l'utilisent, mais posez-vous les questions suivantes:

  • Votre application doit-elle inclure les contacts du profil professionnel par défaut ou l'utilisateur doit-il activer cette option ?
  • Comment le mélange ou la séparation des contacts des profils professionnel et personnel affecte-t-il le parcours de l'utilisateur ?
  • Quel est l'impact d'un appui accidentel sur un contact du profil professionnel ?
  • Qu'advient-il de l'interface de votre application lorsque les contacts du profil professionnel ne sont pas disponibles ?

Votre application doit indiquer clairement un contact du profil professionnel. Vous pouvez peut-être ajouter un badge au contact à l'aide d'une icône de travail familière, comme une mallette.

Capture d'écran montrant des résultats de recherche sous forme de liste
Figure 1. Comment l'application Google Contacts sépare les contacts du profil professionnel

Par exemple, l'application Google Contacts (illustrée dans la figure 1) effectue les opérations suivantes pour lister une combinaison de contacts de profils professionnels et personnels:

  1. Insère un sous-titre pour séparer les sections professionnelle et personnelle de la liste.
  2. Badges des contacts professionnels avec une icône en forme de mallette.
  3. Ouvre un contact professionnel dans le profil professionnel lorsque l'utilisateur appuie dessus.

Si l'utilisateur de l'appareil désactive le profil professionnel, votre application ne peut pas rechercher les coordonnées du profil professionnel ni des annuaires de contacts distants de l'organisation. Selon la façon dont vous utilisez les contacts du profil professionnel, vous pouvez les laisser de côté ou désactiver les commandes de l'interface utilisateur.

Autorisations

Si votre application fonctionne déjà avec les contacts de l'utilisateur, vous disposez de l'autorisation READ_CONTACTS (ou éventuellement WRITE_CONTACTS) que vous avez demandée dans le fichier manifeste de votre application. Étant donné que la même personne utilise le profil personnel et le profil professionnel, vous n'avez pas besoin d'une autorisation supplémentaire pour accéder aux données de contact du profil professionnel.

Un administrateur informatique peut bloquer le profil professionnel qui partage les coordonnées avec le profil personnel. Si un administrateur informatique bloque l'accès, vos recherches de contacts renvoient des résultats vides. Votre application n'a pas besoin de gérer des erreurs spécifiques si l'utilisateur a désactivé le profil professionnel. Le fournisseur de contenu de l'annuaire continue de renvoyer des informations sur les répertoires de contacts professionnels de l'utilisateur (voir la section Répertoires). Pour tester ces autorisations, consultez la section Développement et tests.

Recherche de contacts

Vous pouvez récupérer les contacts du profil professionnel à l'aide des mêmes API et processus que ceux utilisés par votre application pour obtenir les contacts du profil personnel. L'URI d'entreprise pour les contacts est compatible avec Android 7.0 (niveau d'API 24) ou version ultérieure. Vous devez apporter les ajustements suivants à l'URI:

  1. Définissez l'URI du fournisseur de contenu sur Contacts.ENTERPRISE_CONTENT_FILTER_URI, puis indiquez le nom du contact sous forme de chaîne de requête.
  2. Définir un annuaire de contacts à rechercher. Par exemple, ENTERPRISE_DEFAULT recherche les contacts dans le magasin local du profil professionnel.

La modification de l'URI fonctionne avec n'importe quel mécanisme de fournisseur de contenu, tel que CursorLoader. Cette approche est idéale pour charger des données de contact dans des interfaces utilisateur, car l'accès aux données s'effectue sur un thread de nœud de calcul. Pour plus de simplicité, les exemples de ce guide appellent ContentResolver.query(). Pour rechercher des contacts dans le répertoire local des contacts du profil professionnel:

Kotlin

// First confirm the device user has given permission for the personal profile.
// There isn't a separate work permission, but an IT admin can block access.
val readContactsPermission =
  ContextCompat.checkSelfPermission(getBaseContext(), Manifest.permission.READ_CONTACTS)
if (readContactsPermission != PackageManager.PERMISSION_GRANTED) {
  return
}

// Fetch Jackie, James, & Jason (and anyone else whose names begin with "ja").
val nameQuery = Uri.encode("ja")

// Build the URI to look up work profile contacts whose name matches. Query
// the default work profile directory which is the locally-stored contacts.
val contentFilterUri =
  ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
    .buildUpon()
    .appendPath(nameQuery)
    .appendQueryParameter(
      ContactsContract.DIRECTORY_PARAM_KEY,
      ContactsContract.Directory.ENTERPRISE_DEFAULT.toString()
    )
    .build()

// Query the content provider using the generated URI.
var cursor =
  getContentResolver()
    .query(
      contentFilterUri,
      arrayOf(
        ContactsContract.Contacts._ID,
        ContactsContract.Contacts.LOOKUP_KEY,
        ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
      ),
      null,
      null,
      null
    )

// Print any results found using the work profile contacts' display name.
cursor?.use {
  while (it.moveToNext()) {
    Log.i(TAG, "Work profile contact: ${it.getString(2)}")
  }
}

Java

// First confirm the device user has given permission for the personal profile.
// There isn't a separate work permission, but an IT admin can block access.
int readContactsPermission = ContextCompat.checkSelfPermission(
    getBaseContext(), Manifest.permission.READ_CONTACTS);
if (readContactsPermission != PackageManager.PERMISSION_GRANTED) {
  return;
}

// Fetch Jackie, James, & Jason (and anyone else whose names begin with "ja").
String nameQuery = Uri.encode("ja");

// Build the URI to look up work profile contacts whose name matches. Query
// the default work profile directory which is the locally stored contacts.
Uri contentFilterUri = ContactsContract.Contacts.ENTERPRISE_CONTENT_FILTER_URI
    .buildUpon()
    .appendPath(nameQuery)
    .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
        String.valueOf(ContactsContract.Directory.ENTERPRISE_DEFAULT))
    .build();

// Query the content provider using the generated URI.
Cursor cursor = getContentResolver().query(
    contentFilterUri,
    new String[] {
        ContactsContract.Contacts._ID,
        ContactsContract.Contacts.LOOKUP_KEY,
        ContactsContract.Contacts.DISPLAY_NAME_PRIMARY
    },
    null,
    null,
    null);
if (cursor == null) {
  return;
}

// Print any results found using the work profile contacts' display name.
try {
  while (cursor.moveToNext()) {
    Log.i(TAG, "Work profile contact: " + cursor.getString(2));
  }
} finally {
  cursor.close();
}

Répertoires

De nombreuses organisations utilisent des annuaires distants, tels que Microsoft Exchange ou LDAP, qui contiennent les coordonnées de toute l'organisation. Votre application peut aider les utilisateurs à communiquer et à partager des contenus avec les collègues figurant dans l'annuaire de leur organisation. Notez que ces répertoires contiennent généralement des milliers de contacts et que votre application a également besoin d'une connexion réseau active pour effectuer des recherches dans ces annuaires. Vous pouvez utiliser le fournisseur de contenu Directory pour obtenir les répertoires utilisés par les comptes de l'utilisateur et en savoir plus sur un annuaire individuel.

Interrogez le fournisseur de contenu Directory.ENTERPRISE_CONTENT_URI pour obtenir les répertoires du profil personnel et du profil professionnel renvoyés ensemble. La recherche de répertoires du profil professionnel est compatible avec Android 7.0 (niveau d'API 24) ou version ultérieure. Votre application doit toujours autoriser l'utilisateur à accorder à READ_CONTACTS l'autorisation de travailler avec son annuaire de contacts.

Étant donné qu'Android stocke les coordonnées dans différents types de répertoires locaux et distants, la classe Directory propose des méthodes que vous pouvez appeler pour en savoir plus sur un répertoire:

isEnterpriseDirectoryId()
Appelez cette méthode pour savoir si l'annuaire provient d'un compte de profil professionnel. N'oubliez pas que le fournisseur de contenu ENTERPRISE_CONTENT_URI renvoie simultanément les annuaires des profils personnel et professionnel.
isRemoteDirectoryId()
Appelez cette méthode pour savoir si le répertoire est distant. Les annuaires distants peuvent être des magasins de contact d'entreprise ou les réseaux sociaux de l'utilisateur.

L'exemple suivant montre comment utiliser ces méthodes pour filtrer les répertoires de profils professionnels:

Kotlin

// First, confirm the device user has given READ_CONTACTS permission.
// This permission is still needed for directory listings ...

// Query the content provider to get directories for BOTH the personal and
// work profiles.
val cursor =
  getContentResolver()
    .query(
      ContactsContract.Directory.ENTERPRISE_CONTENT_URI,
      arrayOf(ContactsContract.Directory._ID, ContactsContract.Directory.PACKAGE_NAME),
      null,
      null,
      null
    )

// Print the package name of the work profile's local or remote contact directories.
cursor?.use {
  while (it.moveToNext()) {
    val directoryId = it.getLong(0)
    if (ContactsContract.Directory.isEnterpriseDirectoryId(directoryId)) {
      Log.i(TAG, "Directory: ${it.getString(1)}")
    }
  }
}

Java

// First, confirm the device user has given READ_CONTACTS permission.
// This permission is still needed for directory listings ...

// Query the content provider to get directories for BOTH the personal and
// work profiles.
Cursor cursor = getContentResolver().query(
    ContactsContract.Directory.ENTERPRISE_CONTENT_URI,
    new String[]{
        ContactsContract.Directory._ID,
        ContactsContract.Directory.PACKAGE_NAME
    },
    null,
    null,
    null);
if (cursor == null) {
  return;
}

// Print the package name of the work profile's local or remote contact directories.
try {
  while (cursor.moveToNext()) {
    long directoryId = cursor.getLong(0);

    if (ContactsContract.Directory.isEnterpriseDirectoryId(directoryId)) {
      Log.i(TAG, "Directory: " + cursor.getString(1));
    }
  }
} finally {
  cursor.close();
}

L'exemple extrait l'ID et le nom du package pour le répertoire. Pour afficher une interface utilisateur qui aide les utilisateurs à choisir la source d'un annuaire de contacts, vous devrez peut-être récupérer plus d'informations sur l'annuaire. Pour afficher d'autres champs de métadonnées éventuellement disponibles, consultez la documentation de référence de la classe Directory.

Recherches téléphoniques

Les applications peuvent interroger PhoneLookup.CONTENT_FILTER_URI pour rechercher efficacement les coordonnées d'un numéro de téléphone. Vous pouvez obtenir des résultats de recherche auprès du fournisseur de contacts personnels et professionnels si vous remplacez cet URI par PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI. Cet URI de contenu de profil professionnel est disponible sur Android 5.0 (niveau d'API 21) ou version ultérieure.

L'exemple suivant montre une application qui interroge l'URI de contenu du profil professionnel pour configurer l'interface utilisateur pour un appel entrant:

Kotlin

fun onCreateIncomingConnection(
  connectionManagerPhoneAccount: PhoneAccountHandle,
  request: ConnectionRequest
): Connection {
  var request = request
  // Get the telephone number from the incoming request URI.
  val phoneNumber = this.extractTelephoneNumber(request.address)

  var displayName = "Unknown caller"
  var isCallerInWorkProfile = false

  // Look up contact details for the caller in the personal and work profiles.
  val lookupUri =
    Uri.withAppendedPath(
      ContactsContract.PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
      Uri.encode(phoneNumber)
    )
  val cursor =
    getContentResolver()
      .query(
        lookupUri,
        arrayOf(
          ContactsContract.PhoneLookup._ID,
          ContactsContract.PhoneLookup.DISPLAY_NAME,
          ContactsContract.PhoneLookup.CUSTOM_RINGTONE
        ),
        null,
        null,
        null
      )

  // Use the first contact found and check if they're from the work profile.
  cursor?.use {
    if (it.moveToFirst() == true) {
      displayName = it.getString(1)
      isCallerInWorkProfile = ContactsContract.Contacts.isEnterpriseContactId(it.getLong(0))
    }
  }

  // Return a configured connection object for the incoming call.
  val connection = MyAudioConnection()
  connection.setCallerDisplayName(displayName, TelecomManager.PRESENTATION_ALLOWED)

  // Our app's activity uses this value to decide whether to show a work badge.
  connection.setIsCallerInWorkProfile(isCallerInWorkProfile)

  // Configure the connection further ...
  return connection
}

Java

public Connection onCreateIncomingConnection (
    PhoneAccountHandle connectionManagerPhoneAccount, ConnectionRequest request) {
  // Get the telephone number from the incoming request URI.
  String phoneNumber = this.extractTelephoneNumber(request.getAddress());

  String displayName = "Unknown caller";
  boolean isCallerInWorkProfile = false;

  // Look up contact details for the caller in the personal and work profiles.
  Uri lookupUri = Uri.withAppendedPath(
      ContactsContract.PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI,
      Uri.encode(phoneNumber));
  Cursor cursor = getContentResolver().query(
      lookupUri,
      new String[]{
          ContactsContract.PhoneLookup._ID,
          ContactsContract.PhoneLookup.DISPLAY_NAME,
          ContactsContract.PhoneLookup.CUSTOM_RINGTONE
      },
      null,
      null,
      null);

  // Use the first contact found and check if they're from the work profile.
  if (cursor != null) {
    try {
      if (cursor.moveToFirst() == true) {
        displayName = cursor.getString(1);
        isCallerInWorkProfile =
            ContactsContract.Contacts.isEnterpriseContactId(cursor.getLong(0));
      }
    } finally {
      cursor.close();
    }
  }

  // Return a configured connection object for the incoming call.
  MyConnection connection = new MyConnection();
  connection.setCallerDisplayName(displayName, TelecomManager.PRESENTATION_ALLOWED);

  // Our app's activity uses this value to decide whether to show a work badge.
  connection.setIsCallerInWorkProfile(isCallerInWorkProfile);

  // Configure the connection further ...
  return connection;
}

Recherches d'adresses e-mail

Votre application peut obtenir les données de contact personnelles ou professionnelles d'une adresse e-mail en interrogeant Email.ENTERPRISE_CONTENT_LOOKUP_URI. Lorsque vous interrogez cette URL, vous lancez d'abord une recherche dans les contacts personnels pour trouver une correspondance exacte. Si le fournisseur ne correspond à aucun contact personnel, il recherche une correspondance dans les contacts professionnels. Cet URI est disponible sur Android 6.0 (niveau d'API 23) ou version ultérieure.

Pour rechercher les coordonnées d'une adresse e-mail:

Kotlin

// Build the URI to look up contacts from the personal and work profiles that
// are an exact (case-insensitive) match for the email address.
val emailAddress = "somebody@example.com"
val contentFilterUri =
  Uri.withAppendedPath(
    ContactsContract.CommonDataKinds.Email.ENTERPRISE_CONTENT_LOOKUP_URI,
    Uri.encode(emailAddress)
  )

// Query the content provider to first try to match personal contacts and,
// if none are found, then try to match the work contacts.
val cursor =
  contentResolver.query(
    contentFilterUri,
    arrayOf(
      ContactsContract.CommonDataKinds.Email.CONTACT_ID,
      ContactsContract.CommonDataKinds.Email.ADDRESS,
      ContactsContract.Contacts.DISPLAY_NAME
    ),
    null,
    null,
    null
  )
    ?: return

// Print the name of the matching contact. If we want to work-badge contacts,
// we can call ContactsContract.Contacts.isEnterpriseContactId() with the ID.
cursor.use {
  while (it.moveToNext()) {
    Log.i(TAG, "Matching contact: ${it.getString(2)}")
  }
}

Java

// Build the URI to look up contacts from the personal and work profiles that
// are an exact (case-insensitive) match for the email address.
String emailAddress = "somebody@example.com";
Uri contentFilterUri = Uri.withAppendedPath(
    ContactsContract.CommonDataKinds.Email.ENTERPRISE_CONTENT_LOOKUP_URI,
    Uri.encode(emailAddress));

// Query the content provider to first try to match personal contacts and,
// if none are found, then try to match the work contacts.
Cursor cursor = getContentResolver().query(
    contentFilterUri,
    new String[]{
        ContactsContract.CommonDataKinds.Email.CONTACT_ID,
        ContactsContract.CommonDataKinds.Email.ADDRESS,
        ContactsContract.Contacts.DISPLAY_NAME
    },
    null,
    null,
    null);
if (cursor == null) {
  return;
}

// Print the name of the matching contact. If we want to work-badge contacts,
// we can call ContactsContract.Contacts.isEnterpriseContactId() with the ID.
try {
  while (cursor.moveToNext()) {
    Log.i(TAG, "Matching contact: " + cursor.getString(2));
  }
} finally {
  cursor.close();
}

Afficher un contact professionnel

Les applications exécutées dans le profil personnel peuvent afficher une fiche de contact dans le profil professionnel. Appelez ContactsContract.QuickContact.showQuickContact() sur Android 5.0 ou version ultérieure pour lancer l'application Contacts dans le profil professionnel et afficher la fiche du contact.

Pour générer un URI correct pour le profil professionnel, vous devez appeler ContactsContract.Contacts.getLookupUri() et transmettre un ID de contact et une clé de recherche. L'exemple suivant montre comment obtenir l'URI, puis afficher la carte:

Kotlin

// Query the content provider using the ENTERPRISE_CONTENT_FILTER_URI address.
// We use the _ID and LOOKUP_KEY columns to generate a work-profile URI.
val cursor =
  getContentResolver()
    .query(
      contentFilterUri,
      arrayOf(ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY),
      null,
      null
    )

// Show the contact details card in the work profile's Contacts app. The URI
// must be created with getLookupUri().
cursor?.use {
  if (it.moveToFirst() == true) {
    val uri = ContactsContract.Contacts.getLookupUri(it.getLong(0), it.getString(1))
    ContactsContract.QuickContact.showQuickContact(
      activity,
      Rect(20, 20, 100, 100),
      uri,
      ContactsContract.QuickContact.MODE_LARGE,
      null
    )
  }
}

Java

// Query the content provider using the ENTERPRISE_CONTENT_FILTER_URI address.
// We use the _ID and LOOKUP_KEY columns to generate a work-profile URI.
Cursor cursor = getContentResolver().query(
    contentFilterUri,
    new String[] {
        ContactsContract.Contacts._ID,
        ContactsContract.Contacts.LOOKUP_KEY,
    },
    null,
    null,
    null);
if (cursor == null) {
  return;
}

// Show the contact details card in the work profile's Contacts app. The URI
// must be created with getLookupUri().
try {
  if (cursor.moveToFirst() == true) {
    Uri uri = ContactsContract.Contacts.getLookupUri(
        cursor.getLong(0), cursor.getString(1));
    ContactsContract.QuickContact.showQuickContact(
        getActivity(),
        new Rect(20, 20, 100, 100),
        uri,
        ContactsContract.QuickContact.MODE_LARGE,
        null);
  }
} finally {
  cursor.close();
}

Disponibilité

Le tableau suivant récapitule les versions d'Android compatibles avec les données de contact du profil personnel:

Version d'Android Assistance
5.0 (niveau d'API 21) Recherchez les noms des contacts professionnels pour les numéros de téléphone à l'aide de PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.
6.0 (niveau d'API 23) Rechercher les adresses e-mail des contacts professionnels avec Email.ENTERPRISE_CONTENT_LOOKUP_URI
7.0 (niveau d'API 24) Interrogez les noms des contacts professionnels dans les annuaires professionnels à l'aide de Contacts.ENTERPRISE_CONTENT_FILTER_URI.
Listez tous les annuaires des profils professionnel et personnel à l'aide de Directory.ENTERPRISE_CONTENT_URI.

Développement et tests

Pour créer un profil professionnel, procédez comme suit:

  1. Installez l'application Test DPC.
  2. Ouvrez l'application Configurer Test DPC (et non l'icône de l'application Test DPC).
  3. Suivez les instructions à l'écran pour configurer un profil géré.
  4. Dans le profil professionnel, ouvrez l'application Contacts et ajoutez des exemples de contacts.

Pour simuler le blocage de l'accès aux contacts du profil professionnel par un administrateur informatique, procédez comme suit:

  1. Dans le profil professionnel, ouvrez l'application Test DPC.
  2. Recherchez le paramètre Désactiver la recherche de contacts dans tous les profils ou Désactiver l'affichage du numéro de l'appelant dans tous les profils.
  3. Activez le paramètre.

Pour en savoir plus sur le test de votre application avec des profils professionnels, consultez Tester la compatibilité de votre application avec les profils professionnels.

Ressources supplémentaires

Pour en savoir plus sur les contacts ou le profil professionnel, consultez les ressources suivantes: