仕事用プロファイルの連絡先

このデベロッパー ガイドでは、連絡先を追加してアプリを強化する方法について説明します。 仕事用プロファイルからデータが取り込まれます。Android の連絡先 API を使用したことがない場合 連絡先プロバイダを読んで、 説明します。

概要

仕事用プロファイルが設定されたデバイスでは、連絡先が別のローカルに保存されます ディレクトリを作成します。デフォルトでは、アプリが 仕事用の連絡先は表示されません。ただし、 仕事用プロファイルから連絡先情報にアクセスする。たとえば、 これは Google の Android 連絡帳アプリです 作業ディレクトリの連絡先が検索結果に表示されます。

ユーザーは多くの場合、個人のデバイスやアプリを仕事で使いたいと考えています。方法 アプリをユーザーの業務に組み込むことができます。

ユーザー エクスペリエンス

アプリで仕事用プロファイルの連絡先情報をどのように表示するかを検討します。 最適なアプローチは、アプリの性質やユーザーが ただし、次の点を考慮してください。

  • アプリにデフォルトで仕事用プロファイルの連絡先を含めるか、それともユーザーが オプトインするか
  • 仕事用プロファイルの連絡先と個人用プロファイルの連絡先を混在または分離した場合、 どうすればよいでしょうか。
  • 仕事用プロファイルの連絡先を誤ってタップした場合、どのような影響がありますか?
  • 仕事用プロファイルの連絡先が不明な場合のアプリのインターフェースへの影響 どうすればよいでしょうか。

仕事用プロファイルの連絡先をアプリに明示する必要があります。たとえば、 ブリーフケースなど、おなじみの仕事用アイコンを使用します。

検索結果のリスト表示のスクリーンショット
図 1. Google コンタクト アプリで仕事用プロファイルが分離される仕組み 連絡先

たとえば、Google 連絡帳アプリ(図 1)は次の処理を行います。 仕事用プロファイルの連絡先と個人用プロファイルの連絡先を混在させる

  1. リストの仕事用と個人用のセクションを分ける小見出しを挿入します。
  2. ブリーフケースのアイコンが付いた仕事用の連絡先のバッジ。
  3. タップすると、仕事用プロファイルで仕事用の連絡先が開きます。

デバイスを使用しているユーザーが仕事用プロファイルをオフにした場合、アプリは 仕事用プロファイルや組織のリモートから連絡先情報を検索する 連絡先電話番号などです仕事用プロファイルの連絡先の使用方法に応じて、次のことができます。 通知なくこれらの連絡先を除外します。または ユーザーインターフェースを無効にしたり できます。

権限

アプリがすでにユーザーの連絡先と連携している場合は、ユーザーの連絡先が表示されます。 READ_CONTACTS(または WRITE_CONTACTS)でリクエストした権限の付与を アプリ マニフェスト ファイルを使用します。個人用プロファイルと仕事用のプロファイルを同じユーザーが使用しているため 仕事用のプロファイルの連絡先データにアクセスしても 選択します。

IT 管理者は ブロック 仕事用プロファイルが個人用プロファイルと連絡先情報を共有する場合。IT 部門が 管理者がアクセスをブロックした場合、連絡先の検索は空の結果として返されます。お客様の ユーザーが処理をオフにした場合、アプリは特定のエラーを処理する必要がない 選択します。ディレクトリ コンテンツ プロバイダは、 ユーザーの仕事用連絡先ディレクトリ(ディレクトリ セクションを参照) これらの権限をテストするには、開発とテストに関するページ 。

連絡先の検索

仕事用プロファイルから連絡先を取得するには、Google アカウントと同じ API とプロセスを使用します。 アプリが個人用プロファイルの連絡先を取得するために使用するものです。のエンタープライズ URI 連絡先は、Android 7.0(API レベル 24)以降でサポートされています。必要な URI を次のように調整しました。

  1. コンテンツ プロバイダ URI を次のように設定します。 Contacts.ENTERPRISE_CONTENT_FILTER_URI クエリ文字列として連絡先の名前を指定します。
  2. 検索する連絡先ディレクトリを設定します。たとえば ENTERPRISE_DEFAULT が仕事から連絡先を見つける 選択します。

URI の変更は、 CursorLoader - 連絡先データをユーザー インターフェースに読み込む場合に適しています。理由は次のとおりです。 データアクセスはワーカースレッドで行われますわかりやすくするため、この例では、 ガイド呼び出し ContentResolver.query()。新しい P-MAX キャンペーンを 仕事用プロファイルのローカルの連絡先ディレクトリに登録されています。

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

ディレクトリ

多くの組織では、Microsoft Exchange や LDAP などのリモート ディレクトリを使用しており、 組織全体の連絡先情報を含めます役立つアプリ ユーザーは、組織のスペースで見つけた同僚とやり取りしたり、 されます。通常、これらのディレクトリには数千もの連絡先が格納され、 検索するには有効なネットワーク接続も必要です。次を使用: Directory コンテンツ プロバイダ: ディレクトリの詳細を確認できます。

Directory.ENTERPRISE_CONTENT_URI に対してクエリを実行する コンテンツ プロバイダに送り、個人用プロファイルと仕事用ディレクトリからディレクトリを プロフィールが一緒に返されます。仕事用プロファイルのディレクトリの検索は、 Android 7.0(API レベル 24)以降。この場合も、ユーザーはアプリに READ_CONTACTS(連絡先に関する操作の権限) ディレクトリを作成します。

Android では、連絡先情報がさまざまなタイプのローカル / ロケーションと Directory クラスには、リモート ディレクトリから検索するために呼び出すことができるメソッドがあります。 ディレクトリについて:

isEnterpriseDirectoryId()
ディレクトリが仕事用プロファイル アカウントのものであるかどうかを確認するには、このメソッドを呼び出します。 ENTERPRISE_CONTENT_URI コンテンツ プロバイダが連絡先を返すことを 個人用プロファイルと仕事用プロファイルの ディレクトリの両方を作成できます
isRemoteDirectoryId()
ディレクトリがリモートかどうかを確認するには、このメソッドを呼び出します。リモート ディレクトリ 企業の連絡先ストアであったり、ユーザーのソーシャル・ネットワークであったりします。

次の例は、これらのメソッドを使用して仕事用プロファイルをフィルタする方法を示しています。 ディレクトリ:

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

この例では、ディレクトリの ID とパッケージ名を取得します。ユーザーを表示するには ユーザーが連絡先ディレクトリのソースを選択できるようにする場合は、 ディレクトリの詳細情報を取得します。他のメタデータ フィールドを表示するには、 Directory クラス リファレンスをご覧ください。

電話番号検索

アプリがクエリを実行できる PhoneLookup.CONTENT_FILTER_URI で効率的に 電話番号について連絡先データを検索する検索結果は次の場所から取得できます: 個人用プロファイルと仕事用プロファイルの両方の連絡先プロバイダ PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI。 この仕事用プロファイルのコンテンツ URI は、Android 5.0(API レベル 21)または 高くなります。

次の例は、仕事用プロファイルのコンテンツ URI をクエリして、 着信用のユーザー インターフェースを構成するには:

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

メール検索

アプリでは、クエリを実行することで、メールアドレスの個人用または仕事用の連絡先データを取得できます Email.ENTERPRISE_CONTENT_LOOKUP_URI。 この URL に対してクエリを実行すると、まず個人の連絡先で完全一致が検索されます。条件 一致する連絡先がない場合、プロバイダは 一致する連絡先を探すことができます。この URI は Android 6.0(API レベル 23)で使用できます。 以上です。

メールアドレスの連絡先情報を検索する方法は次のとおりです。

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

仕事用の連絡先を表示する

個人用プロファイルで実行されているアプリは、仕事用プロファイルに連絡先カードを表示できます。 発信 ContactsContract.QuickContact.showQuickContact() インチ Android 5.0 以降で、仕事用プロファイルで連絡帳アプリを起動して 連絡先のカードに表示されます。

仕事用プロファイルの正しい URI を生成するには、次を呼び出す必要があります。 ContactsContract.Contacts.getLookupUri() を使用し、 照合キーが含まれています次の例は、URI を取得する方法を示しています。 カードを表示します。

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

対象

次の表は、仕事用プロファイルをサポートする Android バージョンをまとめたものです 個人用プロファイルの連絡先データ:

Android バージョン サポート
5.0(API レベル 21) PhoneLookup.ENTERPRISE_CONTENT_FILTER_URI を使用して、仕事用の連絡先の名前で電話番号を検索します。
6.0(API レベル 23) Email.ENTERPRISE_CONTENT_LOOKUP_URI を使用して、仕事用の連絡先の名前でメールアドレスを検索します。
7.0(API レベル 24) Contacts.ENTERPRISE_CONTENT_FILTER_URI を使用して、仕事用ディレクトリから仕事用連絡先の名前をクエリします。
Directory.ENTERPRISE_CONTENT_URI を使用して、仕事用プロファイルと個人用プロファイル内のすべてのディレクトリを一覧表示します。

開発とテスト

仕事用プロファイルを作成する手順は次のとおりです。

  1. Test DPC アプリをインストールします。
  2. [Set up Test DPC] アプリを開きます(Test DPC アプリのアイコンではありません)。
  3. 画面の手順に沿って管理対象プロファイルを設定します。
  4. 仕事用プロファイルで連絡先アプリを開き、サンプルの連絡先をいくつか追加します。

IT 管理者による仕事用プロファイルの連絡先へのアクセスのブロックをシミュレーションする手順は次のとおりです。

  1. 仕事用プロファイルで Test DPC アプリを開きます。
  2. [クロス プロファイル連絡先検索を無効にする] 設定または [クロス プロファイル発信者番号を無効にする] 設定を無効にする
  3. 設定をオンに切り替えます。

仕事用プロファイルを使ったアプリのテストについて詳しくは、 仕事用プロファイルとの互換性

参考情報

連絡先や仕事用プロファイルについて詳しくは、以下のリソースをご覧ください。