Contatti del profilo di lavoro

Questa guida per gli sviluppatori spiega come migliorare l'app per l'utilizzo dei dati dei contatti del profilo di lavoro. Se non hai mai utilizzato le API per i contatti di Android, consulta Provider di contatti per acquisire familiarità con le API.

Panoramica

I dispositivi con profili di lavoro memorizzano i contatti in directory locali separate per i profili di lavoro e personale. Per impostazione predefinita, quando un'app viene eseguita nel profilo personale, i contatti di lavoro non vengono visualizzati. Tuttavia, un'app può accedere ai dati di contatto dal profilo di lavoro. Ad esempio, un'app che esegue queste operazioni è l'app Contatti Android di Google, che nei risultati di ricerca mostra sia i contatti personali sia i contatti della directory di lavoro.

Gli utenti spesso vogliono utilizzare i loro dispositivi e le loro app personali per lavoro. Utilizzando i contatti del profilo di lavoro, la tua app può diventare parte della giornata lavorativa dell'utente.

Esperienza utente

Valuta in che modo la tua app potrebbe presentare le informazioni di contatto del profilo di lavoro. L'approccio migliore dipende dalla natura della tua app e dal motivo per cui gli utenti la utilizzano, ma tieni in considerazione i seguenti aspetti:

  • L'app deve includere i contatti del profilo di lavoro per impostazione predefinita o l'utente deve attivare l'opzione?
  • In che modo mescolare o separare i contatti del profilo personale e di lavoro influirà sul flusso dell'utente?
  • Qual è l'impatto di toccare accidentalmente un contatto del profilo di lavoro?
  • Cosa succede all'interfaccia dell'app quando i contatti del profilo di lavoro non sono disponibili?

L'app deve indicare chiaramente un contatto del profilo di lavoro. Ad esempio, puoi indicare il contatto con un'icona di lavoro familiare, come una valigetta.

Screenshot che mostra i risultati di ricerca in un elenco
Figura 1. Separazione dei contatti del profilo di lavoro nell'app Contatti Google

Ad esempio, l'app Contatti Google (mostrata nella figura 1) svolge le seguenti operazioni per elencare una combinazione di contatti del profilo di lavoro e personale:

  1. Inserisce un sottotitolo per separare le sezioni lavorative e personali dell'elenco.
  2. Badge dei contatti di lavoro con l'icona di una valigetta.
  3. Apre un contatto di lavoro nel profilo di lavoro quando viene toccato.

Se la persona che utilizza il dispositivo disattiva il profilo di lavoro, l'app non sarà in grado di cercare le informazioni di contatto del profilo di lavoro o delle directory dei contatti remoti dell'organizzazione. A seconda di come utilizzi i contatti del profilo di lavoro, puoi escludere questi contatti in silenzio o potresti dover disattivare i controlli dell'interfaccia utente.

Autorizzazioni

Se la tua app funziona già con i contatti dell'utente, avrai l'autorizzazione READ_CONTACTS (o eventualmente WRITE_CONTACTS) che hai richiesto nel file manifest dell'app. Poiché la stessa persona utilizza il profilo personale e il profilo di lavoro, non sono necessarie ulteriori autorizzazioni per accedere ai dati di contatto dal profilo di lavoro.

Un amministratore IT può bloccare il profilo di lavoro condividendo le informazioni di contatto con il profilo personale. Se un amministratore IT blocca l'accesso, le ricerche dei contatti vengono restituite come risultati vuoti. L'app non deve gestire errori specifici se l'utente ha disattivato il profilo di lavoro. Il fornitore di contenuti della directory continua a restituire informazioni sulle directory dei contatti di lavoro dell'utente (consulta la sezione Directory). Per testare queste autorizzazioni, consulta la sezione Sviluppo e test.

Ricerche di contatti

Puoi recuperare i contatti dal profilo di lavoro utilizzando le stesse API e gli stessi processi utilizzati dalla tua app per inserire i contatti nel profilo personale. L'URI aziendale per i contatti è supportato in Android 7.0 (livello API 24) o versioni successive. Devi apportare le seguenti modifiche all'URI:

  1. Imposta l'URI del fornitore di contenuti su Contacts.ENTERPRISE_CONTENT_FILTER_URI e fornisci il nome del contatto come stringa di query.
  2. Imposta una directory di contatti per la ricerca. Ad esempio, ENTERPRISE_DEFAULT trova i contatti nel negozio locale del profilo di lavoro.

La modifica dell'URI funziona con qualsiasi meccanismo di fornitore di contenuti come un CursorLoader, ideale per caricare i dati di contatto nelle interfacce utente perché l'accesso ai dati avviene su un thread di lavoro. Per semplicità, gli esempi in questa guida chiamano ContentResolver.query(). Ecco come trovare i contatti nella directory dei contatti locale del profilo di lavoro:

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();
}

Elenchi

Molte organizzazioni utilizzano directory remote, come Microsoft Exchange o LDAP, che contengono informazioni di contatto per l'intera organizzazione. L'app può aiutare gli utenti a comunicare e condividere contenuti con i colleghi di lavoro che si trovano nella directory dell'organizzazione. Tieni presente che queste directory in genere contengono migliaia di contatti e che per la ricerca è necessaria anche una connessione di rete attiva. Puoi utilizzare il fornitore di contenuti Directory per ottenere le directory utilizzate dagli account dell'utente e ottenere ulteriori informazioni su una singola directory.

Interroga il fornitore di contenuti Directory.ENTERPRISE_CONTENT_URI per ottenere le directory del profilo personale e del profilo di lavoro restituiti insieme. La ricerca nelle directory del profilo di lavoro è supportata in Android 7.0 (livello API 24) o versioni successive. Per la tua app è comunque necessario che l'utente conceda le autorizzazioni a READ_CONTACTS per lavorare con le sue directory dei contatti.

Poiché Android archivia le informazioni di contatto in diversi tipi di directory locali e remote, la classe Directory offre metodi che puoi chiamare per trovare ulteriori informazioni su una directory:

isEnterpriseDirectoryId()
Chiama questo metodo per scoprire se la directory proviene da un account del profilo di lavoro. Ricorda che il fornitore di contenuti ENTERPRISE_CONTENT_URI restituisce le directory di contatto per il profilo personale e quello di lavoro.
isRemoteDirectoryId()
Chiama questo metodo per scoprire se la directory è remota. Le directory remote possono essere negozi di contatti aziendali o social network dell'utente.

L'esempio seguente mostra come utilizzare questi metodi per filtrare le directory dei profili di lavoro:

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'esempio recupera l'ID e il nome del pacchetto della directory. Per visualizzare un'interfaccia utente che aiuti gli utenti a scegliere una sorgente per la directory dei contatti, potresti dover recuperare ulteriori informazioni sulla directory. Per visualizzare altri campi di metadati che potrebbero essere disponibili, leggi il riferimento alla classe Directory.

Ricerche di numeri di telefono

Le app possono inviare query a PhoneLookup.CONTENT_FILTER_URI per cercare in modo efficiente i dati di contatto per un numero di telefono. Puoi ottenere risultati di ricerca dal provider di contatti dei profili personali e di lavoro se sostituisci questo URI con PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI. L'URI dei contenuti del profilo di lavoro è disponibile su Android 5.0 (livello API 21) o versioni successive.

L'esempio seguente mostra un'app che esegue query sull'URI dei contenuti del profilo di lavoro per configurare l'interfaccia utente per una chiamata in arrivo:

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;
}

Ricerche di email

La tua app può recuperare dati di contatto personali o di lavoro per un indirizzo email eseguendo una query su Email.ENTERPRISE_CONTENT_LOOKUP_URI. Se esegui una query su questo URL, per prima cosa viene cercata una corrispondenza esatta nei contatti personali. Se il fornitore non corrisponde ad alcun contatto personale, cerca una corrispondenza nei contatti di lavoro. Questo URI è disponibile in Android 6.0 (livello API 23) o versioni successive.

Ecco come cercare i dati di contatto per un indirizzo email:

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();
}

Mostra un contatto di lavoro

Le app in esecuzione nel profilo personale possono mostrare una scheda contatto nel profilo di lavoro. Chiama ContactsContract.QuickContact.showQuickContact() in Android 5.0 o versioni successive per avviare l'app Contatti nel profilo di lavoro e visualizzare la scheda del contatto.

Per generare un URI corretto per il profilo di lavoro, devi chiamare ContactsContract.Contacts.getLookupUri() e passare un ID contatto e una chiave di ricerca. L'esempio seguente mostra come recuperare l'URI e mostrare la scheda:

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à

La seguente tabella riassume le versioni di Android che supportano i dati di contatto del profilo di lavoro nel profilo personale:

Versione di Android Assistenza
5.0 (livello API 21) Cerca i nomi dei contatti di lavoro per i numeri di telefono utilizzando PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.
6.0 (livello API 23) Cerca i nomi dei contatti di lavoro per gli indirizzi email utilizzando Email.ENTERPRISE_CONTENT_LOOKUP_URI.
7.0 (livello API 24) Esegui una query sui nomi dei contatti di lavoro dalle directory di lavoro utilizzando Contacts.ENTERPRISE_CONTENT_FILTER_URI.
Elenca tutte le directory nei profili di lavoro e personale utilizzando Directory.ENTERPRISE_CONTENT_URI.

Sviluppo e test

Per creare un profilo di lavoro, segui questi passaggi:

  1. Installa la nostra app Test DPC.
  2. Apri l'app Configura DPC di prova (non l'icona dell'app Test DPC).
  3. Segui le istruzioni sullo schermo per configurare un profilo gestito.
  4. Nel profilo di lavoro, apri l'app Contatti e aggiungi alcuni contatti di esempio.

Per simulare un amministratore IT che blocca l'accesso ai contatti del profilo di lavoro, segui questi passaggi:

  1. Nel profilo di lavoro, apri l'app Test DPC (Test DPC).
  2. Cerca l'impostazione Disattiva ricerca contatti in più profili o Disattiva ID chiamante con più profili.
  3. Imposta l'opzione su On.

Per saperne di più su come testare l'app con i profili di lavoro, leggi Testare la compatibilità dell'app con i profili di lavoro.

Risorse aggiuntive

Per saperne di più sui contatti o sul profilo di lavoro, consulta queste risorse: