Kontak profil kerja

Panduan developer ini menjelaskan cara meningkatkan kualitas aplikasi untuk menggunakan data kontak dari profil kerja. Jika Anda belum pernah menggunakan API kontak Android, baca Penyedia Kontak untuk memahami API tersebut.

Ringkasan

Perangkat dengan profil kerja menyimpan kontak di direktori lokal yang terpisah untuk profil kerja dan pribadi. Secara default, saat berjalan di profil pribadi, aplikasi tidak akan menampilkan kontak kerja. Namun, aplikasi dapat mengakses informasi kontak dari profil kerja. Misalnya, aplikasi yang melakukannya adalah aplikasi Kontak Android Google yang menampilkan kontak pribadi dan direktori kerja di hasil penelusuran.

Pengguna sering kali ingin menggunakan perangkat dan aplikasi pribadi mereka untuk bekerja. Dengan menggunakan kontak profil kerja, aplikasi Anda dapat menjadi bagian dari hari kerja pengguna.

Pengalaman pengguna

Pertimbangkan bagaimana aplikasi Anda dapat menampilkan informasi kontak dari profil kerja. Pendekatan terbaik bergantung pada sifat aplikasi Anda dan alasan orang menggunakannya, tetapi pertimbangkan hal berikut:

  • Haruskah aplikasi Anda menyertakan kontak profil kerja secara default atau harus pengguna ikut serta?
  • Bagaimana pengaruh penggabungan atau pemisahan kontak profil kerja dan pribadi terhadap alur pengguna?
  • Apa dampak dari ketukan kontak profil kerja secara tidak sengaja?
  • Apa yang terjadi pada antarmuka aplikasi Anda saat kontak profil kerja tidak tersedia?

Aplikasi Anda harus menunjukkan kontak profil kerja dengan jelas. Mungkin Anda dapat memberi badge pada kontak menggunakan ikon kantor yang sudah dikenal—seperti koper.

Screenshot yang menampilkan hasil penelusuran dalam daftar
Gambar 1. Cara aplikasi Google Kontak memisahkan kontak profil kerja

Sebagai contoh, aplikasi Google Kontak (ditunjukkan pada gambar 1) melakukan hal berikut untuk mencantumkan campuran kontak profil kerja dan pribadi:

  1. Menyisipkan subheader untuk memisahkan bagian pekerjaan dan pribadi dalam daftar.
  2. Lencana kontak kerja dengan ikon koper.
  3. Membuka kontak kerja di profil kerja saat diketuk.

Jika orang yang menggunakan perangkat menonaktifkan profil kerja, aplikasi Anda tidak dapat mencari informasi kontak dari profil kerja atau direktori kontak jarak jauh organisasi. Bergantung pada cara Anda menggunakan kontak profil kerja, Anda dapat tidak menggunakan kontak ini secara otomatis atau Anda mungkin perlu menonaktifkan kontrol antarmuka pengguna.

Izin

Jika aplikasi sudah berfungsi dengan kontak pengguna, Anda akan memiliki izin READ_CONTACTS (atau mungkin WRITE_CONTACTS) yang Anda minta di file manifes aplikasi. Karena orang yang sama menggunakan profil pribadi dan profil kerja, Anda tidak memerlukan izin lebih lanjut untuk mengakses data kontak dari profil kerja.

Admin IT dapat memblokir informasi kontak berbagi profil kerja dengan profil pribadi. Jika admin IT memblokir akses, penelusuran kontak Anda akan ditampilkan sebagai hasil kosong. Aplikasi Anda tidak perlu menangani error tertentu jika pengguna menonaktifkan profil kerja. Penyedia konten direktori terus menampilkan informasi tentang direktori kontak kerja pengguna (lihat bagian Direktori). Untuk menguji izin ini, lihat bagian Pengembangan dan pengujian.

Penelusuran kontak

Anda bisa mendapatkan kontak dari profil kerja menggunakan API dan proses yang sama dengan yang digunakan aplikasi Anda untuk mendapatkan kontak di profil pribadi. URI perusahaan untuk kontak didukung di Android 7.0 (API level 24) atau yang lebih tinggi. Anda perlu melakukan penyesuaian berikut pada URI:

  1. Tetapkan URI penyedia konten ke Contacts.ENTERPRISE_CONTENT_FILTER_URI, dan berikan nama kontak sebagai string kueri.
  2. Tetapkan direktori kontak yang akan ditelusuri. Misalnya, ENTERPRISE_DEFAULT menemukan kontak di toko lokal profil kerja.

Mengubah URI akan berfungsi dengan mekanisme penyedia konten apa pun seperti CursorLoader—ideal untuk memuat data kontak ke antarmuka pengguna karena akses data terjadi pada thread pekerja. Agar lebih mudah, contoh dalam panduan ini memanggil ContentResolver.query(). Berikut ini cara menemukan kontak di direktori kontak lokal pada profil kerja:

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

Direktori

Banyak organisasi menggunakan direktori jarak jauh, seperti Microsoft Exchange atau LDAP, yang berisi informasi kontak untuk seluruh organisasi. Aplikasi Anda dapat membantu pengguna berkomunikasi dan berbagi dengan rekan kerja yang ada di direktori organisasi mereka. Perhatikan bahwa direktori ini biasanya berisi ribuan kontak, dan aplikasi Anda juga memerlukan koneksi jaringan aktif untuk menelusurinya. Anda dapat menggunakan penyedia konten Directory untuk mendapatkan direktori yang digunakan oleh akun pengguna dan mencari tahu lebih lanjut tentang masing-masing direktori.

Buat kueri penyedia konten Directory.ENTERPRISE_CONTENT_URI untuk mendapatkan direktori dari profil pribadi dan profil kerja yang ditampilkan bersama. Penelusuran direktori profil kerja didukung di Android 7.0 (API level 24) atau yang lebih tinggi. Aplikasi Anda masih mengharuskan pengguna memberikan izin READ_CONTACTS agar dapat digunakan dengan direktori kontaknya.

Karena Android menyimpan informasi kontak dalam berbagai jenis direktori lokal dan jarak jauh, class Directory memiliki metode yang dapat Anda panggil untuk mengetahui informasi selengkapnya tentang direktori:

isEnterpriseDirectoryId()
Panggil metode ini untuk mengetahui apakah direktori berasal dari akun profil kerja. Perlu diingat bahwa penyedia konten ENTERPRISE_CONTENT_URI menampilkan direktori kontak untuk profil pribadi dan kerja secara bersamaan.
isRemoteDirectoryId()
Panggil metode ini untuk mengetahui apakah direktori tersebut bersifat jarak jauh. Direktori jarak jauh dapat berupa toko kontak perusahaan atau jaringan sosial pengguna.

Contoh berikut menunjukkan cara menggunakan metode ini untuk memfilter direktori profil kerja:

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

Contoh ini akan mengambil ID dan nama paket untuk direktori. Untuk menampilkan antarmuka pengguna yang membantu pengguna memilih sumber direktori kontak, Anda mungkin perlu mengambil informasi lebih lanjut tentang direktori tersebut. Untuk melihat kolom metadata lain yang mungkin tersedia, baca referensi class Directory.

Pencarian ponsel

Aplikasi dapat mengkueri PhoneLookup.CONTENT_FILTER_URI untuk mencari data kontak untuk nomor telepon secara efisien. Anda bisa mendapatkan hasil pencarian dari penyedia kontak pribadi dan profil kerja jika Anda mengganti URI ini dengan PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI. URI konten profil kerja ini tersedia di Android 5.0 (API level 21) atau yang lebih tinggi.

Contoh berikut menunjukkan aplikasi yang membuat kueri URI konten profil kerja untuk mengonfigurasi antarmuka pengguna untuk panggilan masuk:

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

Pencarian email

Aplikasi Anda dapat memperoleh data kontak pribadi atau kantor untuk alamat email dengan membuat kueri Email.ENTERPRISE_CONTENT_LOOKUP_URI. Membuat kueri URL ini terlebih dahulu akan menelusuri kontak pribadi untuk pencocokan persis. Jika penyedia tidak cocok dengan kontak pribadi mana pun, penyedia akan menelusuri kontak kerja untuk menemukan kecocokan. URI ini tersedia di Android 6.0 (API level 23) atau yang lebih tinggi.

Berikut adalah cara mencari informasi kontak untuk alamat 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();
}

Menampilkan kontak bisnis

Aplikasi yang berjalan di profil pribadi dapat menampilkan kartu kontak di profil kerja. Panggil ContactsContract.QuickContact.showQuickContact() di Android 5.0 atau yang lebih baru untuk meluncurkan Aplikasi kontak di profil kerja dan menampilkan kartu kontak.

Agar dapat membuat URI yang benar untuk profil kerja, Anda harus memanggil ContactsContract.Contacts.getLookupUri() serta meneruskan ID kontak dan kunci pencarian. Contoh berikut menunjukkan cara mendapatkan URI, lalu menampilkan kartu:

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

Ketersediaan

Tabel berikut meringkas versi Android mana yang mendukung data kontak profil kerja di profil pribadi:

Versi Android Dukung
5.0 (Level API 21) Cari nama kontak kantor untuk nomor telepon menggunakan PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI.
6.0 (Level API 23) Cari nama kontak kantor untuk alamat email menggunakan Email.ENTERPRISE_CONTENT_LOOKUP_URI.
7.0 (Level API 24) Buat kueri nama kontak kerja dari direktori kerja menggunakan Contacts.ENTERPRISE_CONTENT_FILTER_URI.
Cantumkan semua direktori di profil kerja dan pribadi menggunakan Directory.ENTERPRISE_CONTENT_URI.

Pengembangan dan pengujian

Untuk membuat profil kerja, ikuti langkah-langkah berikut:

  1. Instal aplikasi Test DPC.
  2. Buka aplikasi Siapkan Test DPC (bukan ikon aplikasi DPC Pengujian).
  3. Ikuti petunjuk di layar untuk menyiapkan profil terkelola.
  4. Di profil kerja, buka aplikasi Kontak dan tambahkan beberapa contoh kontak.

Untuk menyimulasikan akses admin IT yang memblokir akses ke kontak profil kerja, ikuti langkah-langkah berikut:

  1. Di profil kerja, buka aplikasi Test DPC.
  2. Telusuri setelan Nonaktifkan penelusuran kontak antar-profil atau setelan Nonaktifkan ID penelepon antar-profil.
  3. Ubah posisi setelan ke Aktif.

Untuk mempelajari lebih lanjut cara menguji aplikasi dengan profil kerja, baca Menguji Aplikasi Anda untuk Kompatibilitas dengan Profil Kerja.

Referensi lainnya

Untuk mempelajari kontak atau profil kerja lebih lanjut, lihat referensi berikut: