İş profilindeki kişiler

Bu geliştirici kılavuzunda, uygulamanızı iş profilindeki kişi verilerini kullanacak şekilde nasıl geliştirebileceğiniz açıklanmaktadır. Android'in kişiler API'lerini daha önce kullanmadıysanız API'ler hakkında bilgi edinmek için Contact Provider'ı (Kişi Sağlayıcı) okuyun.

Genel bakış

İş profiline sahip cihazlar, kişileri iş ve kişisel profiller için ayrı yerel dizinlerde depolar. Varsayılan olarak, bir uygulama kişisel profilde çalıştığında iş kişilerini göstermez. Ancak, uygulamalar iş profilinden kişi bilgilerine erişebilir. Örneğin, bunu yapan bir uygulama, arama sonuçlarında hem kişisel hem de iş dizini kişilerini gösteren Google'ın Android Kişiler uygulamasıdır.

Kullanıcılar genellikle iş için kişisel cihazlarını ve uygulamalarını kullanmak ister. İş profilindeki kişileri kullanarak uygulamanız kullanıcılarınızın iş gününün bir parçası haline gelebilir.

Kullanıcı deneyimi

Uygulamanızın iş profilindeki iletişim bilgilerini nasıl sunabileceğini düşünün. En iyi yaklaşım, uygulamanızın yapısına ve kullanıcıların onu kullanma nedenine bağlıdır ancak şunları göz önünde bulundurun:

  • Uygulamanız varsayılan olarak iş profili kişilerini dahil etmeli mi yoksa kullanıcı izin vermeli mi?
  • İş ve özel profil kişilerini karıştırmak veya ayırmak kullanıcının akışını nasıl etkiler?
  • İş profilindeki kişilerden birine yanlışlıkla dokunmanın etkisi nedir?
  • İş profili kişileri kullanılamıyorsa uygulamanızın arayüzüne ne olur?

Uygulamanızda iş profili kişisi açıkça belirtilmelidir. Mesela evrak çantası gibi tanıdık bir iş simgesini kullanarak ya da dilediğiniz kişiye rozet verebilirsiniz.

Arama sonuçlarını liste halinde gösteren ekran görüntüsü
Şekil 1. Google Kişiler uygulaması iş profilindeki kişileri nasıl ayırır?

Örneğin, Google Kişiler uygulaması (şekil 1'de gösterilmiştir) iş ve özel profil kişilerini bir arada listelemek için aşağıdakileri yapar:

  1. Listenin iş ve kişisel bölümlerini ayırmak için bir alt başlık ekler.
  2. Rozetler, kişilerde evrak çantası simgesiyle çalışır.
  3. Dokunulduğunda iş profilinde bir iş kişisi açar.

Cihazı kullanan kişi iş profilini kapatırsa uygulamanız iş profilindeki veya kuruluşun uzaktan kişi dizinlerindeki kişi bilgilerini arayamaz. İş profili kişilerini kullanma şeklinize bağlı olarak bu kişileri sessizce dışarıda bırakabilirsiniz veya kullanıcı arayüzü denetimlerini devre dışı bırakmanız gerekebilir.

İzinler

Uygulamanız zaten kullanıcının kişileriyle çalışıyorsa uygulama manifest dosyanızda kullanıcının READ_CONTACTS (veya muhtemelen WRITE_CONTACTS) iznini almış olursunuz. Aynı kişi hem kişisel profili hem de iş profilini kullandığından, iş profilindeki kişi verilerine erişmek için daha fazla izne ihtiyacınız yoktur.

BT yöneticisi, kişisel profille iletişim bilgilerini paylaşan iş profilini engelleyebilir. BT yöneticisi erişimi engellerse kişi aramalarınız boş sonuçlar olarak döndürülür. Kullanıcı iş profilini kapatmışsa uygulamanızın belirli hataları işlemesi gerekmez. Dizin içeriği sağlayıcı, kullanıcının iş irtibatı dizinleri hakkında bilgi döndürmeye devam eder (Dizinler bölümüne bakın). Bu izinleri test etmek için Geliştirme ve test bölümüne bakın.

Kişi aramaları

Uygulamanızın kişileri kişisel profilde almak için kullandığı API'leri ve süreçleri kullanarak iş profilindeki kişileri alabilirsiniz. Kişiler için kurumsal URI, Android 7.0 (API düzeyi 24) veya sonraki sürümlerde desteklenir. URI'da aşağıdaki ayarlamaları yapmanız gerekir:

  1. İçerik sağlayıcı URI'sını Contacts.ENTERPRISE_CONTENT_FILTER_URI olarak ayarlayın ve kişinin adını sorgu dizesi olarak sağlayın.
  2. Aranacak kişi dizinini ayarlayın. Örneğin, ENTERPRISE_DEFAULT iş profilinin yerel mağazasındaki kişileri bulur.

URI'nın değiştirilmesi, CursorLoader gibi herhangi bir içerik sağlayıcı mekanizmasıyla çalışır. Veri erişimi bir çalışan iş parçacığında gerçekleştiğinden, kişi verilerinin kullanıcı arayüzlerine yüklenmesi için idealdir. Kolay anlaşılması için bu kılavuzdaki örnekler ContentResolver.query(). İş profilinin yerel kişi dizininde kişileri şu şekilde bulabilirsiniz:

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

Dizinler

Birçok kuruluş, kuruluşun tamamı için iletişim bilgilerini içeren Microsoft Exchange veya LDAP gibi uzak dizinler kullanır. Uygulamanız, kullanıcıların kuruluşlarının dizininde bulunan iş arkadaşlarıyla iletişim kurmasına ve paylaşımda bulunmasına yardımcı olabilir. Bu dizinlerde genellikle binlerce kişi bulunduğunu ve uygulamanızın bu kişileri aramak için etkin bir ağ bağlantısına ihtiyacı olduğunu hatırlatmak isteriz. Kullanıcı hesapları tarafından kullanılan dizinleri almak ve tek bir dizin hakkında daha fazla bilgi edinmek için Directory içerik sağlayıcısını kullanabilirsiniz.

Kişisel profilden ve birlikte döndürülen iş profilinden dizin almak için Directory.ENTERPRISE_CONTENT_URI içerik sağlayıcısını sorgulayın. İş profili dizinlerini arama özelliği Android 7.0 (API düzeyi 24) veya sonraki sürümlerde desteklenmektedir. Kullanıcının hâlâ iletişim dizinleriyle çalışması için READ_CONTACTS'e izin vermesi gerekiyor.

Android, iletişim bilgilerini farklı yerel ve uzak dizin türlerinde depoladığından Directory sınıfı, bir dizin hakkında daha fazla bilgi edinmek için çağırabileceğiniz yöntemlere sahiptir:

isEnterpriseDirectoryId()
Dizinin bir iş profili hesabına ait olup olmadığını öğrenmek için bu yöntemi çağırın. ENTERPRISE_CONTENT_URI içerik sağlayıcısının, kişisel ve iş profili için iletişim dizinlerini birlikte döndürdüğünü unutmayın.
isRemoteDirectoryId()
Dizinin uzak olup olmadığını öğrenmek için bu yöntemi çağırın. Uzak dizinler, kurumsal iletişim mağazaları veya kullanıcının sosyal ağları olabilir.

Aşağıdaki örnekte, iş profili dizinlerini filtrelemek için bu yöntemleri nasıl kullanabileceğiniz gösterilmektedir:

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

Örnek, dizin için kimlik ve paket adını getirir. Kullanıcıların kişi dizini kaynağı seçmesine yardımcı olan bir kullanıcı arayüzü görüntülemek için dizin hakkında daha fazla bilgi getirmeniz gerekebilir. Kullanılabilecek diğer meta veri alanlarını görmek için Directory sınıfı referansını okuyun.

Telefon aramaları

Uygulamalar, bir telefon numarasının kişi verilerini verimli bir şekilde aramak için PhoneLookup.CONTENT_FILTER_URI uygulamasını sorgulayabilir. Bu URI'yi PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI ile değiştirirseniz hem kişisel hem de iş profili kişileri sağlayıcısından arama sonuçları alabilirsiniz. Bu iş profili içerik URI'si, Android 5.0 (API düzeyi 21) veya sonraki sürümlerde kullanılabilir.

Aşağıdaki örnekte, gelen bir çağrı için kullanıcı arayüzünü yapılandırmak üzere iş profili içerik URI'sını sorgulayan bir uygulama gösterilmektedir:

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

E-posta aramaları

Uygulamanız, Email.ENTERPRISE_CONTENT_LOOKUP_URI sorgusunu sorgulayarak bir e-posta adresi için kişisel veya iş kişi verilerini alabilir. Bu URL sorgulandığında önce özel kişiler arasında tam eşleşme aranır. Sağlayıcı, hiçbir özel kişiyle eşleşmiyorsa eşleşme için iş kişilerini arar. Bu URI, Android 6.0 (API düzeyi 23) veya sonraki sürümlerde kullanılabilir.

Bir e-posta adresine ilişkin iletişim bilgilerini nasıl bulabileceğiniz aşağıda açıklanmıştır:

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

İş iletişim bilgilerini göster

Kişisel profilde çalışan uygulamalar iş profilinde kişi kartı gösterebilir. İş profilinde Kişiler uygulamasını başlatmak ve kişinin kartını göstermek için Android 5.0 veya sonraki sürümlerde ContactsContract.QuickContact.showQuickContact() numaralı telefonu arayın.

İş profili için doğru URI'yi oluşturmak için ContactsContract.Contacts.getLookupUri() yöntemini çağırmanız ve bir kişi kimliği ile arama anahtarı iletmeniz gerekir. Aşağıdaki örnekte, URI'yı nasıl alıp ardından kartı gösterebileceğiniz gösterilmektedir:

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

Kullanılabilirlik

Aşağıdaki tabloda, kişisel profilde iş profili kişi verilerini destekleyen Android sürümleri özetlenmektedir:

Android sürümü Destek
5.0 (API düzeyi 21) Telefon numaraları için iş kişilerinin adlarını PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI kullanarak arayın.
6.0 (API düzeyi 23) Email.ENTERPRISE_CONTENT_LOOKUP_URI kullanarak e-posta adresleri için iş irtibatları arayın.
7.0 (API düzeyi 24) Contacts.ENTERPRISE_CONTENT_FILTER_URI kullanarak iş dizinlerindeki kişi adlarını sorgulayın.
Directory.ENTERPRISE_CONTENT_URI kullanarak iş profilindeki ve kişisel profillerdeki tüm dizinleri listeleyin.

Geliştirme ve test

İş profili oluşturmak için aşağıdaki adımları uygulayın:

  1. Test DPC uygulamamızı yükleyin.
  2. Test DPC'yi kur uygulamasını açın (Test DPC uygulamasının simgesini değil).
  3. Yönetilen profil oluşturmak için ekrandaki talimatları uygulayın.
  4. İş profilinde Kişiler uygulamasını açın ve örnek kişiler ekleyin.

İş profili kişilerine erişimi engelleyen bir BT yöneticisinin simülasyonunu yapmak için aşağıdaki adımları uygulayın:

  1. İş profilinde Test DPC uygulamasını açın.
  2. Profiller arası kişi aramayı devre dışı bırak ayarını veya Profiller arası arayan kimliğini devre dışı bırak ayarını arayın.
  3. Ayarı Açık konumuna getirin.

Uygulamanızı iş profilleriyle test etme hakkında daha fazla bilgi edinmek için Uygulamanızı İş Profilleriyle Uyumluluk açısından test etme başlıklı makaleyi okuyun.

Ek kaynaklar

Kişiler veya iş profili hakkında daha fazla bilgi edinmek için şu kaynaklara bakın: