Contacts du profil professionnel

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

Présentation

Les appareils dotés d'un profil professionnel stockent les contacts dans un espace de stockage local distinct. 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 du profil professionnel. Par exemple, une application qui il s'agit de l'application Contacts de Google qui affiche à la fois 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 contacts du profil professionnel, votre application peut faire partie de la journée de travail de vos utilisateurs.

Expérience utilisateur

Réfléchissez à la manière dont votre application peut présenter les coordonnées du profil professionnel. La meilleure approche dépend de la nature de votre application et de la raison pour laquelle les utilisateurs l'utiliser, mais pensez aux points suivants:

  • Votre application doit-elle inclure les contacts du profil professionnel par défaut ou si l'utilisateur à activer ?
  • En quoi la combinaison ou la séparation des contacts professionnels et personnels a-t-elle une incidence sur du flux utilisateur ?
  • Que se passe-t-il si vous appuyez accidentellement sur un contact d'un 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 badge le contact à l'aide d'une icône de travail familière, comme une mallette.

Capture d'écran montrant les résultats de recherche dans une liste
Figure 1. Séparation des profils professionnels par l'application Google Contacts contacts

Par exemple, l'application Google Contacts (voir figure 1) effectue les opérations suivantes pour : afficher à la fois des contacts professionnels et personnels:

  1. Insère un sous-titre pour séparer les sections professionnelle et personnelle de la liste.
  2. Badge 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 la personne qui utilise l'appareil désactive le profil professionnel, votre application ne pourra pas rechercher des coordonnées dans le profil professionnel ou dans le journal de bord de l'organisation des annuaires de contacts. Selon la façon dont vous utilisez les contacts du profil professionnel, vous pouvez : omettez ces contacts de manière silencieuse ou vous devrez peut-être désactiver l'interface utilisateur .

Autorisations

Si votre application fonctionne déjà avec les contacts de l'utilisateur, vous disposez de ses READ_CONTACTS (ou éventuellement WRITE_CONTACTS) que vous avez demandée dans votre fichier manifeste de l'application. Parce qu'une 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 profil.

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

Recherches de contacts

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

  1. Définir l'URI du fournisseur de contenu sur Contacts.ENTERPRISE_CONTENT_FILTER_URI, et indiquez le nom du contact sous forme de chaîne de requête.
  2. Définissez un répertoire de contacts dans lequel effectuer la recherche. Par exemple : ENTERPRISE_DEFAULT trouve des contacts dans le travail le magasin local de la fiche.

La modification de l'URI fonctionne avec n'importe quel mécanisme de fournisseur de contenu, tel qu'un CursorLoader : idéal pour charger des données de contact dans des interfaces utilisateur, car l'accès aux données a lieu sur un thread de nœud de calcul. Par souci de simplicité, les exemples appel de guide ContentResolver.query(). Voici comment trouver contacts figurant 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, comme Microsoft Exchange ou LDAP, contenant les coordonnées de l'ensemble de l'organisation. Votre application peut vous aider les utilisateurs communiquent et partagent avec leurs collègues de travail . Notez que ces répertoires contiennent généralement des milliers de contacts, et votre application a également besoin d'une connexion réseau active pour effectuer des recherches. Vous pouvez utiliser le fournisseur de contenu Directory pour obtenir les répertoires utilisés par le comptes d'utilisateurs et obtenir davantage d'informations sur un annuaire individuel.

Interrogez Directory.ENTERPRISE_CONTENT_URI fournisseur de contenu pour obtenir les annuaires du profil personnel et du profil professionnel profil renvoyé ensemble. La recherche dans les répertoires de profils professionnels est prise en charge dans Android 7.0 (niveau d'API 24) ou version ultérieure Votre application a besoin que l'utilisateur donne Autorisations READ_CONTACTS pour travailler avec ses contacts répertoires.

Android stocke les coordonnées dans différents types annuaires distants, la classe Directory comporte des méthodes que vous pouvez appeler pour en trouver d'autres. à propos d'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 les coordonnées des annuaires de profils personnel et professionnel.
isRemoteDirectoryId()
Appelez cette méthode pour savoir si l'annuaire est distant. Annuaires distants Il peut s'agir de magasins de contacts professionnels ou des réseaux sociaux de l'utilisateur.

L'exemple suivant montre comment utiliser ces méthodes pour filtrer le profil professionnel répertoires:

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 récupère l'ID et le nom de package du répertoire. Afficher un utilisateur interface qui aide les utilisateurs à choisir une source d'annuaire de contacts, vous devrez peut-être récupérer plus d'informations sur le répertoire. Pour voir d'autres champs de métadonnées peuvent être disponibles, consultez la documentation de référence de la classe Directory.

Recherches de numéros de téléphone

Les applications peuvent interroger PhoneLookup.CONTENT_FILTER_URI pour rechercher les coordonnées d'un numéro de téléphone ; Vous pouvez obtenir des résultats de recherche de le fournisseur de contacts des profils 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 plus élevée.

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 coordonnées personnelles ou professionnelles d'une adresse e-mail en interrogeant Email.ENTERPRISE_CONTENT_LOOKUP_URI L'interrogation de cette URL lance d'abord la recherche d'une correspondance exacte dans les contacts personnels. Si le fournisseur ne correspond à aucun contact personnel, il recherche vos contacts professionnels. Cet URI est disponible dans Android 6.0 (niveau d'API 23). ou supérieur.

Pour rechercher les coordonnées d'une adresse e-mail, procédez comme suit:

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. Appeler ContactsContract.QuickContact.showQuickContact() po 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 une l'ID de contact et la clé de recherche. L'exemple suivant montre comment obtenir l'URI puis affichez la fiche:

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 le profil professionnel. coordonnées dans le profil personnel:

Version d'Android Assistance
5.0 (niveau d'API 21) Utilisez PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI pour rechercher les numéros de téléphone des contacts professionnels.
6.0 (niveau d'API 23) Recherchez les noms des contacts professionnels correspondant aux adresses e-mail en utilisant 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.
Répertoriez 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 notre application Test DPC.
  2. Ouvrez l'application Configurer l'outil 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 le paramètre Désactiver le paramètre d'affichage du numéro de l'appelant dans tous les profils
  3. Activez le paramètre.

Pour en savoir plus sur les tests de votre application avec des profils professionnels, consultez Tester votre application pour Compatibilité avec les profils professionnels.

Ressources supplémentaires

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