Contatos do perfil de trabalho

Este guia do desenvolvedor explica como você pode aprimorar seu aplicativo para usar recursos de contato dados do perfil de trabalho. Se você não usou as APIs de contatos do Android consulte Provedor de contatos para se familiarizar com as APIs.

Visão geral

Dispositivos com perfis de trabalho armazenam contatos em locais separados para os perfis de trabalho e pessoal. Por padrão, quando um aplicativo é executado o perfil pessoal, os contatos de trabalho não serão exibidos. No entanto, um app pode acessar os dados de contato do perfil de trabalho. Por exemplo, um app que é o app Contatos do Google no Android, que mostra informações pessoais e do diretório de trabalho nos resultados da pesquisa.

Muitas vezes, os usuários querem usar apps e dispositivos pessoais no trabalho. Usando contatos do perfil de trabalho, seu app poderá se tornar parte do dia de trabalho do usuário.

Experiência do usuário

Pense em como seu app pode apresentar dados de contato do perfil de trabalho. A melhor abordagem depende da natureza do seu aplicativo e da razão pela qual as pessoas usá-lo, mas pense no seguinte:

  • Caso seu app inclua contatos do perfil de trabalho por padrão ou o usuário participar?
  • Como a mistura ou separação dos contatos nos perfis pessoal e de trabalho afetará do fluxo do usuário?
  • Qual é o impacto de tocar acidentalmente em um contato do perfil de trabalho?
  • O que acontece com a interface do app quando os contatos do perfil de trabalho não estão disponíveis?

O app precisa indicar claramente um contato do perfil de trabalho. Talvez você possa usar o selo o contato usando um ícone de trabalho conhecido, como uma pasta.

Captura de tela mostrando os resultados da pesquisa em uma lista
Figura 1. Como o app Contatos do Google separa o perfil de trabalho contatos

Por exemplo, o app Contatos do Google (mostrado na figura 1) faz o seguinte para Liste uma combinação de contatos do perfil pessoal e de trabalho:

  1. Insere um subtítulo para separar as seções de trabalho e pessoal da lista.
  2. Selos de contato de trabalho com um ícone de maleta.
  3. Abre um contato de trabalho no perfil de trabalho quando um usuário toca nele.

Se o usuário do dispositivo desativar o perfil de trabalho, o app não poderá: procurar informações de contato no perfil de trabalho ou no ambiente remoto da organização diretórios de contatos. Dependendo de como você usa os contatos do perfil de trabalho, é possível deixar esses contatos de fora silenciosamente ou talvez seja necessário desativar a interface do usuário controles de segurança.

Permissões

Se seu aplicativo já estiver funcionando com os contatos do usuário, você terá sua READ_CONTACTS (ou possivelmente WRITE_CONTACTS) que você solicitou em arquivo de manifesto do app. Como a mesma pessoa usa o perfil pessoal e o de trabalho perfil de trabalho, não são necessárias outras permissões para acessar os dados de contato do ambiente perfil.

Um administrador de TI pode bloquear o perfil de trabalho que compartilha informações de contato com o perfil pessoal. Se um profissional de TI se o administrador bloquear o acesso, suas pesquisas de contatos serão retornadas como resultados vazios. Seu não precisará processar erros específicos se o usuário desativar o trabalho perfil. O provedor de conteúdo do diretório continua retornando informações sobre o os diretórios de contatos profissionais do usuário (consulte a seção Diretórios). Para testar essas permissões, consulte a seção Desenvolvimento e teste nesta seção.

Pesquisas de contatos

É possível receber contatos do perfil de trabalho usando as mesmas APIs e processos que que seu app usa para receber contatos no perfil pessoal. O URI da empresa para é compatível com o Android 7.0 (nível 24 da API) ou mais recente. Você precisa fazer os seguintes ajustes no URI:

  1. Definir o URI do provedor de conteúdo como Contacts.ENTERPRISE_CONTENT_FILTER_URI, e forneça o nome do contato como uma string de consulta.
  2. Defina um diretório de contatos para pesquisar. Por exemplo: O ENTERPRISE_DEFAULT encontra contatos no trabalho. na loja local do perfil.

A alteração do URI funciona com qualquer mecanismo do provedor de conteúdo, como um CursorLoader: ideal para carregar dados de contato em interfaces do usuário, porque o acesso aos dados acontece em uma linha de execução de worker. Para simplificar, os exemplos chamada de guia ContentResolver.query(). Veja como encontrar contatos no diretório de contatos local do perfil de trabalho:

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

Diretórios

Muitas organizações usam diretórios remotos, como Microsoft Exchange ou LDAP, que contêm informações de contato de toda a organização. Seu app pode ajudar os usuários se comunicam e compartilham com colegas de trabalho diretório. Observe que esses diretórios geralmente contêm milhares de contatos, e o app também precisa de uma conexão de rede ativa para fazer pesquisas. Você pode usar o provedor de conteúdo Directory para acessar os diretórios usados pelo contas de usuário e saber mais sobre um diretório individual.

Consulte o Directory.ENTERPRISE_CONTENT_URI provedor de conteúdo para acessar os diretórios do perfil pessoal e do de perfil sejam retornados juntos. A pesquisa em diretórios do perfil de trabalho pode ser feita em Android 7.0 (nível 24 da API) ou mais recente Seu app ainda precisa que o usuário dê Permissões do READ_CONTACTS para trabalhar com o contato diretórios.

Como o Android armazena dados de contato em diferentes tipos de dados diretórios remotos, a classe Directory tem métodos que podem ser chamados para encontrar mais sobre um diretório:

isEnterpriseDirectoryId()
Chame este método para descobrir se o diretório é de uma conta do perfil de trabalho. O provedor de conteúdo ENTERPRISE_CONTENT_URI retorna o contato para os perfis pessoal e de trabalho juntos.
isRemoteDirectoryId()
Chame este método para descobrir se o diretório é remoto. Diretórios remotos podem ser lojas de contatos corporativos ou podem ser as redes sociais do usuário.

O exemplo a seguir mostra como usar esses métodos para filtrar o perfil de trabalho diretórios:

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

O exemplo busca o ID e o nome do pacote para o diretório. Para mostrar um usuário interface de usuário que ajuda os usuários a escolher a fonte do diretório de contatos, buscar mais informações sobre o diretório. Para ver outros campos de metadados que possam estar disponíveis, leia a referência da classe Directory.

Consultas de telefone

Os apps podem fazer consultas PhoneLookup.CONTENT_FILTER_URI para procurar dados de contato para um número de telefone. Você pode receber resultados de pesquisa provedor de contatos do perfil pessoal e de trabalho se você substituir esse URI por PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI Esse URI de conteúdo do perfil de trabalho está disponível no Android 5.0 (API de nível 21) ou mais alto.

O exemplo a seguir mostra um app que consulta o URI de conteúdo do perfil de trabalho para configurar a interface do usuário para uma chamada recebida:

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

Pesquisas de e-mail

Seu app pode receber dados de contatos pessoais ou de trabalho de um endereço de e-mail consultando Email.ENTERPRISE_CONTENT_LOOKUP_URI Consultar esse URL primeiro faz com que os contatos pessoais pesquisem uma correspondência exata. Se o provedor não corresponder a nenhum contato pessoal, ele pesquisará contatos de trabalho para uma correspondência. Esse URI está disponível no Android 6.0 (API de nível 23) ou superior.

Veja como procurar informações de contato de um endereço de 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();
}

Mostrar um contato de trabalho

Os apps em execução no perfil pessoal podem mostrar um card de contato no perfil de trabalho. Ligação ContactsContract.QuickContact.showQuickContact() pol. Android 5.0 ou superior para iniciar o app Contatos no perfil de trabalho e mostrar o card do contato.

Para gerar um URI correto para o perfil de trabalho, você precisa chamar ContactsContract.Contacts.getLookupUri() e transmita um ID do contato e chave de pesquisa. O exemplo a seguir mostra como conseguir o URI e, em seguida, mostre o cartão:

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

Disponibilidade

A tabela a seguir resume quais versões do Android são compatíveis com o perfil de trabalho dados de contato no perfil pessoal:

Versão do Android Suporte
5.0 (nível 21 da API) Pesquise nomes de contatos de trabalho para números de telefone usando o PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.
6.0 (nível 23 da API) Pesquise nomes de contatos de trabalho para endereços de e-mail usando o Email.ENTERPRISE_CONTENT_LOOKUP_URI.
7.0 (nível 24 da API) Consulte os nomes dos contatos de trabalho nos diretórios de trabalho usando o Contacts.ENTERPRISE_CONTENT_FILTER_URI.
Liste todos os diretórios nos perfis de trabalho e pessoal usando Directory.ENTERPRISE_CONTENT_URI.

Desenvolvimento e teste

Para criar um perfil de trabalho, siga estas etapas:

  1. Instale nosso app de teste de DPC.
  2. Abra o app Configurar o DPC de teste, não o ícone desse app.
  3. Siga as instruções na tela para configurar um perfil gerenciado.
  4. No perfil de trabalho, abra o app Contatos e adicione alguns contatos de exemplo.

Para simular um administrador de TI bloqueando o acesso aos contatos do perfil de trabalho, siga estas etapas:

  1. No perfil de trabalho, abra o app Testar DPC.
  2. Procure a configuração Desativar a pesquisa de contatos entre perfis ou a Desativar o identificador de chamadas entre perfis.
  3. Mude a configuração para Ativada.

Para saber mais sobre como testar seu app com perfis de trabalho, leia Testar seu app para Compatibilidade com perfis de trabalho

Outros recursos

Para saber mais sobre contatos ou perfil de trabalho, consulte estes recursos: