page.title=連絡先プロバイダ @jd:body

クイックビュー

本書の内容

  1. 連絡先プロバイダの構成
  2. 未加工連絡先
  3. データ
  4. 連絡先
  5. 同期アダプタからのデータ
  6. 必要なパーミッション
  7. ユーザー プロファイル
  8. 連絡先プロバイダのメタデータ
  9. 連絡先プロバイダへのアクセス
  10. 連絡先プロバイダの同期アダプタ
  11. ソーシャル ストリーム データ
  12. 連絡先プロバイダの追加機能

キークラス

  1. {@link android.provider.ContactsContract.Contacts}
  2. {@link android.provider.ContactsContract.RawContacts}
  3. {@link android.provider.ContactsContract.Data}
  4. {@code android.provider.ContactsContract.StreamItems}

関連サンプル

  1. Contact Manager
  2. サンプル同期アダプタ

関連ドキュメント

  1. コンテンツ プロバイダの基本

連絡先プロバイダは、人のデータに関する端末の中央リポジトリを管理する、柔軟で効果的な Android コンポーネントです。 連絡先プロバイダは、端末の連絡先アプリケーションに表示されるデータのソースであり、さらに独自アプリケーションで連絡先プロバイダのデータにアクセスし、端末とオンライン サービスとの間でデータを転送することもできます。 このプロバイダは幅広いデータソースに対応しており、各人に関してできるだけ多くのデータを管理しようとするため、複雑な構造をしています。 そのため、連絡先プロバイダの API には、データの取得と変更をどちらもやりやすくするクラスやインターフェースが多数用意されています。

このガイドでは、以下について説明します。

このガイドは、Android のコンテンツ プロバイダの基礎知識がある読者を対象としています。Android のコンテンツ プロバイダについて詳しくは、「コンテンツ プロバイダの基本」をご覧ください。 サンプル同期アダプタ サンプルアプリは、同期アダプタを使用して連絡先プロバイダと Google ウェブ サービスでホストされているサンプル アプリケーションとの間でデータを転送する例です。

連絡先プロバイダの構成

連絡先プロバイダは、Android のコンテンツ プロバイダ コンポーネントです。人に関する 3 種類のデータを保持しており、それぞれがコンテンツ プロバイダによって提供される 1 つのテーブルに対応しています。この構成を図 1 に示します。

図 1. 連絡先プロバイダのテーブル構造。

この 3 つのテーブルは、一般にそれぞれのコントラクト クラス名で呼ばれます。各クラスは、テーブルによって使用されるコンテンツ URI、列名、列の値のための定数を定義しています。

{@link android.provider.ContactsContract.Contacts} テーブル
各行は、未加工連絡先の行の集約に基づいて異なる人を表します。
{@link android.provider.ContactsContract.RawContacts} テーブル
各行には、ユーザーのアカウントとタイプに固有の、人に関するデータの概要が格納されています。
{@link android.provider.ContactsContract.Data} テーブル
各行には、メールアドレスや電話番号など、未加工連絡先の詳細情報が格納されています。

{@link android.provider.ContactsContract} に含まれるコントラクト クラスによって表現される他のテーブルは補助テーブルであり、連絡先プロバイダがその操作を管理したり、端末の連絡先アプリや電話アプリの特定機能をサポートしたりするために使用します。

未加工連絡先

未加工連絡先は、アカウント タイプとアカウント名の 1 つのペアを使用して得られる人に関するデータです。 連絡先プロバイダでは、1 人に関するデータのソースとして複数のオンライン サーバーを使用できるため、同じ個人に対して複数の未加工連絡先が許されます。 未加工連絡先が複数ある場合、ユーザーは同じアカウント タイプの複数のアカウントから取得したその人のデータを組み合わせることができます。

未加工連絡先データの大半は、{@link android.provider.ContactsContract.RawContacts} テーブルには格納されません。 その代わり、{@link android.provider.ContactsContract.Data} テーブルの 1 つまたは複数の行に格納されています。 各データ行には列 {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID Data.RAW_CONTACT_ID} があり、親 {@link android.provider.ContactsContract.RawContacts} 行の {@code android.provider.BaseColumns#_ID RawContacts._ID} 値が格納されています。

重要な未加工連絡先列

表 1 に、{@link android.provider.ContactsContract.RawContacts} の重要な列を示します。 表の後の注もお読みください。

表 1. 重要な未加工連絡先列。

列名 用途
{@link android.provider.ContactsContract.SyncColumns#ACCOUNT_NAME} この未加工連絡先のソースであるアカウント タイプにおけるアカウント名。 たとえば、Google アカウントのアカウント名は、デバイス オーナーの Gmail アドレスのどれかです。 詳しくは、次の {@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} の項目をご覧ください。 この名前の形式は、アカウント タイプによって異なります。必ずしもメールアドレスとは限りません。
{@link android.provider.ContactsContract.SyncColumns#ACCOUNT_TYPE} この未加工連絡先のソースであるアカウント タイプ。たとえば、Google アカウントのアカウント タイプは com.google です。 アカウント タイプは必ず、所有または管理しているドメインのドメイン ID で修飾してください。 そうすることで、アカウント タイプが確実に一意になります。 連絡先データを提供するアカウント タイプには、通常、連絡先プロバイダと同期する関連同期アダプタが用意されています。
{@link android.provider.ContactsContract.RawContactsColumns#DELETED} 未加工連絡先の「削除済み」フラグ。 連絡先プロバイダは、同期アダプタが該当行をサーバーから削除し、最終的にリポジトリから削除するまで、このフラグを基に行を内部的に管理します。

次に、{@link android.provider.ContactsContract.RawContacts} テーブルに関する重要な注を示します。

未加工連絡先データのソース

未加工連絡先の働きを理解するために、「Emily Dickinson」というユーザーについて考えてみましょう。彼女は自分の端末に次の 3 つのアカウントを定義しています。

このユーザーは、[アカウント] の設定でこの 3 つのアカウントすべてについて [連絡先を同期] を有効にしています。

Emily Dickinson がブラウザのウィンドウを開き、Gmail に emily.dickinson@gmail.com としてログインし、[連絡先] を開いて「Thomas Higginson」を追加したとしましょう。 後日、彼女が Gmail に emilyd@gmail.com としてログインし、メールを「Thomas Higginson」宛てに送信すると、彼は自動的に連絡先として追加されます。 彼女はまた、Twitter で「colonel_tom」(Thomas Higginson の Twitter ID)をフォローしています。

連絡先プロバイダは、この操作の結果として次の 3 行の未加工連絡先を作成します。

  1. 「Thomas Higginson」の、emily.dickinson@gmail.com に関連付けられた未加工連絡先。 ユーザー アカウントのタイプは Google です。
  2. 「Thomas Higginson」の、emilyd@gmail.com に関連付けられた新たな未加工連絡先。 ユーザー アカウントのタイプはやはり Google です。名前が前の行とまったく同じなのに新たな未加工連絡先が作成されたのは、この個人が異なるユーザー アカウントに追加されたからです。
  3. 「Thomas Higginson」の、「belle_of_amherst」に関連付けられた未加工連絡先。ユーザー アカウントのタイプは Twitter です。

データ

前にも説明したように、未加工連絡先のデータは未加工連絡先の _ID 値にリンクされている {@link android.provider.ContactsContract.Data} 行に格納されます。 これにより、1 つの未加工連絡先が同じタイプのデータ(メールアドレスや電話番号など)のインスタンスを複数持つことができます。 たとえば、{@code emilyd@gmail.com} に対する「Thomas Higginson」(Google アカウント emilyd@gmail.com に関連付けられた、Thomas Higginson の未加工連絡先の行)には、thigg@gmail.com という個人用アドレスと thomas.higginson@gmail.com という仕事用アドレスがあり、連絡先プロバイダはこの 2 つのメールアドレスの行を格納し、両方とも同じ未加工連絡先にリンクします。

異なるタイプのデータが同じテーブルに格納されることに注目してください。表示名、電話番号、メール、住所、写真、ウェブサイトに関する詳細行はすべて、{@link android.provider.ContactsContract.Data} テーブルにあります。 これを管理しやすくするため、{@link android.provider.ContactsContract.Data} テーブルの一部の行には説明的な名前が、それ以外には一般的な名前がそれぞれ付いています。 説明的な名前が付いた列の内容は、行データのタイプによらず意味が同じなのに対し、一般的な名前が付いた列の内容は、データのタイプによって意味が異なります。

説明的な名前の列

説明的な名前が付いた列の例をいくつか示します。

{@link android.provider.ContactsContract.Data#RAW_CONTACT_ID}
このデータの、未加工連絡先の _ID 列の値。
{@link android.provider.ContactsContract.Data#MIMETYPE}
カスタム MIME タイプで表現した、この行に格納されているデータのタイプ。連絡先プロバイダは、{@link android.provider.ContactsContract.CommonDataKinds} のサブクラスに定義されている MIME タイプを使用します。 これらの MIME タイプはオープンソースで、連絡先プロバイダと連携する任意のアプリケーションまたは同期アダプタで使用できます。
{@link android.provider.ContactsContract.DataColumns#IS_PRIMARY}
このタイプのデータ行が 1 つの未加工連絡先に対して複数発生する場合、{@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} 列はそのタイプに対するプライマリ データを含むデータ行を示します。 たとえば、ユーザーがある連絡先の電話番号を長押しし、[デフォルトに設定] を選択すると、その番号を含む {@link android.provider.ContactsContract.Data} 行の {@link android.provider.ContactsContract.DataColumns#IS_PRIMARY} 列にゼロでない値が設定されます。

汎用名の列

自由に使用できる DATA1DATA15 という 15 個の汎用列の他に、同期アダプタしか使用してはいけない SYNC1SYNC4 という 4 個の列があります。 汎用名列の定数は、行に指定されているデータのタイプによらず、常に機能します。

DATA1 列はインデックス付きです。連絡先プロバイダは常に、この列を、最も頻繁にクエリの対象となるとプロバイダが予想するデータ用の列として使用します。 たとえば、メールの行では、この列には実際のメールアドレスが格納されます。

DATA15 は慣例として、写真サムネイルのようなバイナリ ラージ オブジェクト(BLOB)データ用に予約されています。

タイプ固有の列名

特定タイプの行に含まれる列に対する作業をやりやすくするため、連絡先プロバイダではタイプ固有の列名定数もあり、たとえば {@link android.provider.ContactsContract.CommonDataKinds} のサブクラスに定義されています。 こうした定数は、同じ列名に異なる定数名を充てて、特定タイプの行に含まれるデータにアクセスしやすくしているだけのものです。

たとえば、{@link android.provider.ContactsContract.CommonDataKinds.Email} クラスには、MIME タイプが {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE Email.CONTENT_ITEM_TYPE} である{@link android.provider.ContactsContract.Data} 行向けにタイプ固有の列名定数が定義されています。 このクラスには、メールアドレス 列用に {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} という定数が含まれています。 {@link android.provider.ContactsContract.CommonDataKinds.Email#ADDRESS} の実際の値は「data1」で、これはこの列の汎用名と同じです。

警告: 連絡先プロバイダにあらかじめ定義されている MIME タイプのどれかである行を使用している {@link android.provider.ContactsContract.Data} テーブルには、独自のカスタムデータを追加しないでください。 追加すると、データが失われたり、連絡先プロバイダが誤動作したりすることがあります。 たとえば、メールアドレスではなくユーザー名が格納されている MIME タイプ {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_ITEM_TYPE Email.CONTENT_ITEM_TYPE} の行は、列 DATA1 には追加しないでください。 一方、行に独自のカスタム MIME タイプを使用している場合は、独自のタイプ固有名を定義して列を自由に使用してかまいません。

図 2 に、説明的な名前の列とデータ列で {@link android.provider.ContactsContract.Data} 行がどう見えるか、そしてタイプ固有の列名が汎用列名をどう「オーバーレイ」するかを示します。

How type-specific column names map to generic column names

図 2. タイプ固有の列名と汎用列名

タイプ固有列名が使用されるクラス

表 2 に、最もよく用いられるタイプ固有列名クラスを示します。

表 2. タイプ固有列名が使用されるクラス

対応クラス データのタイプ
{@link android.provider.ContactsContract.CommonDataKinds.StructuredName} このデータ行に関連付けられている未加工連絡先の名前データ。 1 つの未加工連絡先は、この行を 1 行だけ持ちます。
{@link android.provider.ContactsContract.CommonDataKinds.Photo} このデータ行に関連付けられている未加工連絡先のメインの写真。 1 つの未加工連絡先は、この行を 1 行だけ持ちます。
{@link android.provider.ContactsContract.CommonDataKinds.Email} このデータ行に関連付けられている未加工連絡先のメールレアドレス。 1 つの未加工連絡先は複数のメールアドレスを持つことができます。
{@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal} このデータ行に関連付けられている未加工連絡先の住所。 1 つの未加工連絡先は複数の住所を持つことができます。
{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} その未加工連絡先を連絡先プロバイダのグループのどれかにリンクする識別子。 グループは、アカウント タイプとアカウント名のオプション機能です。詳しくは、連絡先グループをご覧ください。

連絡先

連絡先プロバイダは、すべてのアカウント タイプとアカウント名にわたって未加工連絡先の行を結び付けて 1 つの連絡先を形成します。 これにより、1 人の人についてユーザーが集めた全データを表示したり変更したりしやすくなります。 連絡先プロバイダは、新しい連絡先の行の作成と、既存の連絡先の行を使用した未加工連絡先の集約とを管理します。 アプリケーションと同期アダプタは連絡先の追加はできず、連絡先の行の一部の列は読み取り専用です。

注: 連絡先を連絡先プロバイダに {@link android.content.ContentResolver#insert(Uri,ContentValues) insert()} を使用して追加しようとすると、{@link java.lang.UnsupportedOperationException} 例外が発生します。 「読み取り専用」になっている列をアップデートしようとしても、そのアップデートは無視されます。

連絡先プロバイダは、既存の連絡先と一致しない新しい未加工連絡先が追加されると、それに対して新しい連絡先を作成します。 また、既存の未加工連絡先のデータが変更され、それまで関連付けられていた連絡先と一致しなくなった場合にも、同じ処理がなされます。 アプリケーションまたは同期アダプタが、既存の連絡先と一致する新しい未加工連絡先を作成すると、その新しい未加工連絡先は既存の連絡先に集約されます。

連絡先プロバイダは、連絡先の行を未加工連絡先とリンクするのに、{@link android.provider.ContactsContract.Contacts Contacts} テーブルの _ID 列を使用します。 未加工連絡先テーブル {@link android.provider.ContactsContract.RawContacts} の CONTACT_ID 列には、未加工連絡先の各行に関連付けられている連絡先の行の _ID 値が格納されます。

{@link android.provider.ContactsContract.Contacts} テーブルには列 {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} もあり、こちらは連絡先の行への「永久」リンクです。 連絡先プロバイダは連絡先を自動的に管理するため、集約や同期が行われると、それに応じて連絡先の行の {@code android.provider.BaseColumns#_ID} 値が変更される場合があります。 この処理が行われても、コンテンツ URI {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI} と連絡先の {@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} の組み合わせは、引き続きその連絡先の行を指すため、{@code android.provider.ContactsContract.ContactsColumns#LOOKUP_KEY} を使用して「お気に入り」の連絡先へのリンクを管理するなどができます。 この列には、{@code android.provider.BaseColumns#_ID} 列の形式と関係のない独自の形式があります。

図 3 に、3 つのテーブルの相互関係を示します。

Contacts provider main tables

図 3. Contacts、RawContacts、Data の各テーブル間の関係。

同期アダプタからのデータ

ユーザーは端末に連絡先データを直接入力しますが、データはウェブサービスから同期アダプタを経由して連絡先プロバイダにも流れます。同期アダプタは、端末とサービスの間のデータ転送を自動化します。 同期アダフタはシステムの管理下でバックグラウンドで実行され、{@link android.content.ContentResolver} のメソッドを呼び出してデータを管理します。

Android では、同期アダプタと連携するウェブサービスをアカウント タイプで識別します。 各同期アダプタが扱うアカウント タイプは 1 つですが、そのタイプのアカウント名を複数サポートできます。 アカウント名とアカウント タイプについては、未加工連絡先データのソースで簡単に説明しています。 次に、アカウント タイプとアカウント名が同期アダプタやサービスとどのような関係にあるかを詳しく説明します。

アカウント タイプ
ユーザーがデータを格納しているサービスを示します。ほとんどの場合、ユーザーにはサービスに対する認証が求められます。 たとえば、Google Contacts はアカウント タイプの 1 つで、コード google.com で識別されます。 この値は、{@link android.accounts.AccountManager} によって使用されるアカウント タイプに対応します。
アカウント名
あるアカウント タイプで使用する特定のアカウントまたはログインを示します。Google Contacts アカウントは Google アカウントと同じで、アカウント名としてメールアドレスを使用します。 他のサービスでは、1 語のユーザー名や数字の ID が使われていることもあります。

アカウント タイプは、一意である必要はありません。1 人のユーザーが複数の Google Contacts アカウントを設定し、それぞれのデータを連絡先プロバイダにダウンロードする、ということが可能です。このような使い方は、そのユーザーが個人用のアカウント名で私用の連絡先を、仕事用のアカウント名で仕事用の連絡先を管理している場合にありえます。 アカウント名は、普通は一意です。 この 2 つを組み合わせて、連絡先プロバイダと外部サービスとの間のある決まったデータフローを識別します。

独自サービスのデータを連絡先プロバイダに転送する場合は、独自の同期アダプタを作成する必要があります。 詳しくは、連絡先プロバイダの同期アダプタをご覧ください。

図 4 に、人に関するデータの流れにおける連絡先プロバイダの位置付けを示します。 右から 2 列目の各ボックス内のアダプタには、そのアダプタのアカウント タイプが示されています。

Flow of data about people

図 4. 連絡先プロバイダに絡んだデータフロー。

必要なパーミッション

連絡先プロバイダにアクセスするアプリケーションは、次のパーミッションを要求する必要があります。

1 つ以上のテーブルに対する読み取りアクセス
{@link android.Manifest.permission#READ_CONTACTS}。AndroidManifest.xml <uses-permission> 要素を使用して <uses-permission android:name="android.permission.READ_CONTACTS"> のように指定します。
1 つ以上のテーブルに対する書き込みアクセス
{@link android.Manifest.permission#WRITE_CONTACTS}。AndroidManifest.xml <uses-permission> 要素を使用して <uses-permission android:name="android.permission.WRITE_CONTACTS"> のように指定します。

これらのパーミッションは、ユーザー プロファイル データにまでは拡張されません。ユーザー プロファイルとそれに必要なパーミッションについては、次のセクションであるユーザー プロファイルで説明しています。

ユーザーの連絡先データは個人的で秘密性の高い情報であることを再度ご確認ください。ユーザーはプライバシーに敏感であり、アプリケーションが自分や自分の連絡先に関するデータを集めることを望みません。 連絡先データにアクセスするためのパーミッションが必要な理由が明らかでないと、ユーザーがアプリケーションを低く評価したりインストールを拒否したりすることがあります。

ユーザー プロファイル

{@link android.provider.ContactsContract.Contacts} テーブルには、その端末のユーザーのプロファイル データを格納している行が 1 行あります。 このデータが記述しているのは端末の user であって、そのユーザーの連絡先ではありません。 プロファイルの連絡先の行は、プロファイルを使用する各システムの未加工連絡先の行にリンクされています。 プロファイルの未加工連絡先の各行は、複数のデータ行を持つことができます。ユーザー プロファイルにアクセスするための定数は、{@link android.provider.ContactsContract.Profile} クラスに用意されています。

ユーザー プロファイルにアクセスするには、特別なパーミッションが必要です。読み取りと書き込みに必要な {@link android.Manifest.permission#READ_CONTACTS} パーミッションと {@link android.Manifest.permission#WRITE_CONTACTS} パーミッションの他に、ユーザー プロファイルに対する読み取りと書き込みのために {@code android.Manifest.permission#READ_PROFILE} パーミッションと {@code android.Manifest.permission#WRITE_PROFILE} パーミッションがそれぞれ必要です。

ユーザーのプロファイルは秘密性の高い情報であることを再度ご確認ください。パーミッション {@code android.Manifest.permission#READ_PROFILE} を使用すると、端末ユーザーの個人識別データにアクセスできます。 アプリケーションの説明には、ユーザー プロファイルへのアクセス パーミッションが必要な理由を必ず記載してください。

ユーザーのプロファイルが格納された連絡先の行を取得するには、{@link android.content.ContentResolver#query(Uri,String[], String, String[], String) ContentResolver.query()} を呼び出します。 コンテンツ URI を {@link android.provider.ContactsContract.Profile#CONTENT_URI} に設定し、選択条件は何も指定しません。 このコンテンツ URI は、そのプロファイルの未加工連絡先やデータを取得するためのベース URI としても使用できます。 たとえば、次のスニペットは指定されたプロファイルのデータを取得します。

// Sets the columns to retrieve for the user profile
mProjection = new String[]
    {
        Profile._ID,
        Profile.DISPLAY_NAME_PRIMARY,
        Profile.LOOKUP_KEY,
        Profile.PHOTO_THUMBNAIL_URI
    };

// Retrieves the profile from the Contacts Provider
mProfileCursor =
        getContentResolver().query(
                Profile.CONTENT_URI,
                mProjection ,
                null,
                null,
                null);

注: 連絡先の行を複数取得する場合、そのなかの 1 つがユーザー プロファイルかどうかを確認するには、行の {@link android.provider.ContactsContract.ContactsColumns#IS_USER_PROFILE} 列をテストします。 その連絡先がユーザー プロファイルであれば、この列は「1」に設定されています。

連絡先プロバイダのメタデータ

連絡先プロバイダは、連絡先データの状態を継続的に追跡するためのデータをリポジトリで管理します。 リポジトリに関するこのメタデータは、RawContacts、Data、Contacts の各テーブルの行、{@link android.provider.ContactsContract.Settings} テーブル、{@link android.provider.ContactsContract.SyncState} テーブルなど、さまざまな場所に格納されています。 次の表に、各メタデータの効果を示します。

表 3. 連絡先プロバイダに用意されているメタデータ

テーブル 意味
{@link android.provider.ContactsContract.RawContacts} {@link android.provider.ContactsContract.SyncColumns#DIRTY} 「0」 - 前回の同期以降、変更はありません。 端末上で変更があり、サーバーと同期する必要がある未加工連絡先をマークします。 Android アプリケーションが行をアップデートすると、連絡先プロバイダによって値が自動的に設定されます。

未加工連絡先やデータのテーブルを変更する同期アダプタは、使用するコンテンツ URI の末尾に文字列 {@link android.provider.ContactsContract#CALLER_IS_SYNCADAPTER} を必ず追加してください。 これにより、プロバイダが行をダーティとマークするのを防ぐことができます。 こうしないと、同期アダプタによる変更がローカルな変更と認識され、変更のソースがサーバーであるにもかかわらず、その変更がサーバーに送信されます。

「1」 - 前回の同期以降に変更があり、サーバーへの同期が必要です。
{@link android.provider.ContactsContract.RawContacts} {@link android.provider.ContactsContract.SyncColumns#VERSION} この行のバージョン番号。 この行またはその関連データが変更されるたび、連絡先プロバイダがこの値を自動的にインクリメントします。
{@link android.provider.ContactsContract.Data} {@link android.provider.ContactsContract.DataColumns#DATA_VERSION} この行のバージョン番号。 このデータ行が変更されるたび、連絡先プロバイダがこの値を自動的にインクリメントします。
{@link android.provider.ContactsContract.RawContacts} {@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} この未加工連絡先をそれが作成されたアカウントと一意に結び付ける文字列値。 同期アダプタが新しい未加工連絡先を作成するたび、この列はその未加工連絡先に対するサーバーの一意の ID に設定される必要があります。 Android アプリケーションが新しい未加工連絡先を作成した場合、そのアプリケーションはこの列を空欄のままにする必要があります。 同期アダプタはこれを確認して、サーバー上に新しい未加工連絡先を作成し、{@link android.provider.ContactsContract.SyncColumns#SOURCE_ID} の値を取得します。

特に、ソース ID はアカウント タイプごとに一意で、同期中に安定している必要があります。

  • Unique: アカウントの各未加工連絡先には独自のソース ID が必要です。これを強制しないと、連絡先アプリケーションに問題を引き起こすことになります。 同じアカウント タイプに対する 2 つの未加工連絡先が、同じソース ID を持つことがありえます。 たとえば、アカウント {@code emily.dickinson@gmail.com} の未加工連絡先「Thomas Higginson」は、アカウント {@code emilyd@gmail.com} の未加工連絡先「Thomas Higginson」と同じソース ID を持つことができます。
  • Stable: ソース ID は、未加工連絡先のオンライン サービス データにおいて変わらない部分です。 たとえば、ユーザーが [アプリ] 設定から [連絡先ストレージ] を消去し、再同期したとしても、復元された未加工連絡先のソース ID は以前と同じになります。 これを強制しないと、ショートカットが機能しなくなります。
{@link android.provider.ContactsContract.Groups} {@link android.provider.ContactsContract.GroupsColumns#GROUP_VISIBLE} 「0」 - このグループに属する連絡先が Android アプリケーションの UI に表示されなくなります。 この列は、ユーザーが特定のグループに属する連絡先を非表示にすることを許可するサーバーに対して互換性を確保するために用意されています。
「1」 - このグループに属する連絡先を Android アプリケーションの UI に表示できます。
{@link android.provider.ContactsContract.Settings} {@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE} 「0」 - このアカウントとアカウント タイプについて、グループに属さない連絡先は Android アプリケーションの UI に表示されなくなります。 デフォルトでは、グループに属する未加工連絡先がないなら連絡先は表示されません(未加工連絡先のグループ メンバーシップは、{@link android.provider.ContactsContract.Data} テーブルの 1 つないし複数の {@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} 行によって示されます)。 あるアカウントとアカウント タイプについて、{@link android.provider.ContactsContract.Settings} テーブルの行でこのフラグを設定すると、グループのない連絡先を強制的に表示できます。 このフラグの用途の 1 つとして、グループを使用しないサーバーから連絡先を取得して表示することが考えられます。
「0」 - このアカウントとアカウント タイプについて、グループに属さない連絡先が Android アプリケーションの UI に表示されます。
{@link android.provider.ContactsContract.SyncState} (すべて) このテーブルを使用して、同期アダプタのメタデータを格納します。 このテーブルを使用すると、同期状態などの同期関連データを永続的に端末上に格納できます。

連絡先プロバイダへのアクセス

このセクションでは、連絡先プロバイダからのデータにアクセスするためのガイドラインについて、以下に注目して説明します。

同期アダプタからの変更については、連絡先プロバイダの同期アダプタでも詳しく説明しています。

エンティティのクエリ

連絡先プロバイダのテーブルは階層構造になっており、ある行とその「子」の行すべてを取得すると便利なことがよくあります。 たとえば、ある人に関するすべての情報を表示するために、{@link android.provider.ContactsContract.Contacts} 行 1 行に対するすべての {@link android.provider.ContactsContract.RawContacts} 行や、{@link android.provider.ContactsContract.RawContacts} 行 1 行に対するすべての {@link android.provider.ContactsContract.CommonDataKinds.Email} 行を取得することが考えられます。 こうした処理をやりやすくするため、連絡先プロバイダにはエンティティ構造が用意されています。これは、テーブル間でのデータベースの和集合のように機能します。

1 つのエンティティは、ある親テーブルとその子テーブルから選ばれた列からなるテーブルのようなものです。 エンティティをクエリする場合は、そのエンティティで使用できる列に基づいてプロジェクションと検索の条件を指定します。 その結果が {@link android.database.Cursor} で、取得された各子テーブル行につき 1 行を含みます。 たとえば、ある連絡先名と、その名前のすべての未加工連絡先の {@link android.provider.ContactsContract.CommonDataKinds.Email} 行について、{@link android.provider.ContactsContract.Contacts.Entity} をクエリすると、各 {@link android.provider.ContactsContract.CommonDataKinds.Email} 行につき 1 行を含む {@link android.database.Cursor} が得られます。

エンティティにより、クエリが簡素化されます。エンティティを使用することで、ある連絡先または未加工連絡先の連絡先データをすべて取得できるため、まず親テーブルにクエリして ID を取得し、次にその ID 使用して子テーブルにクエリする必要がありません。また、連絡先プロバイダは、エンティティに対するクエリを 1 回のトランザクションで処理することから、取得されたデータの内部的な整合性が保証されます。

注: エンティティには通常、親テーブルと子テーブルのすべての列が含まれるわけではありません。 そのエンティティの列名定数のリストにない列名に対して作業しようとすると、{@link java.lang.Exception} が発生します。

次のスニペットは、ある連絡先のすべての未加工連絡先を取得する方法を示しています。このスニペットは、「メイン」と「詳細」という 2 つのアクティビティを持つ、もっと大きなアプリケーションの一部です。 メイン アクティビティは、連絡先の行の一覧を示します。ユーザーが 1 つを選択すると、このアクティビティはその ID を詳細アクティビティに送ります。 詳細アクティビティは {@link android.provider.ContactsContract.Contacts.Entity} を使用して、選択した連絡先と関連付けられているすべての未加工連絡先から取得したすべてのデータ行を示します。

このスニペットは「詳細」アクティビティからの抜粋です。

...
    /*
     * Appends the entity path to the URI. In the case of the Contacts Provider, the
     * expected URI is content://com.google.contacts/#/entity (# is the ID value).
     */
    mContactUri = Uri.withAppendedPath(
            mContactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY);

    // Initializes the loader identified by LOADER_ID.
    getLoaderManager().initLoader(
            LOADER_ID,  // The identifier of the loader to initialize
            null,       // Arguments for the loader (in this case, none)
            this);      // The context of the activity

    // Creates a new cursor adapter to attach to the list view
    mCursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            mFromColumns,                // the columns in the cursor that provide the data
            mToViews,                    // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    mRawContactList.setAdapter(mCursorAdapter);
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {

    /*
     * Sets the columns to retrieve.
     * RAW_CONTACT_ID is included to identify the raw contact associated with the data row.
     * DATA1 contains the first column in the data row (usually the most important one).
     * MIMETYPE indicates the type of data in the data row.
     */
    String[] projection =
        {
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID,
            ContactsContract.Contacts.Entity.DATA1,
            ContactsContract.Contacts.Entity.MIMETYPE
        };

    /*
     * Sorts the retrieved cursor by raw contact id, to keep all data rows for a single raw
     * contact collated together.
     */
    String sortOrder =
            ContactsContract.Contacts.Entity.RAW_CONTACT_ID +
            " ASC";

    /*
     * Returns a new CursorLoader. The arguments are similar to
     * ContentResolver.query(), except for the Context argument, which supplies the location of
     * the ContentResolver to use.
     */
    return new CursorLoader(
            getApplicationContext(),  // The activity's context
            mContactUri,              // The entity content URI for a single contact
            projection,               // The columns to retrieve
            null,                     // Retrieve all the raw contacts and their data rows.
            null,                     //
            sortOrder);               // Sort by the raw contact ID.
}

読み込みが完了すると、{@link android.app.LoaderManager} は {@link android.app.LoaderManager.LoaderCallbacks#onLoadFinished(Loader, D) onLoadFinished()} に対するコールバックを起動します。 このメソッドへの入力引数の 1 つは、クエリの結果を含む {@link android.database.Cursor} です。 独自アプリでは、データをこの {@link android.database.Cursor} から取得して表示したりさらに処理したりできます。

バッチ変更

可能であれば必ず、連絡先プロバイダのデータを「バッチモード」で挿入、アップデート、削除してください。そのためには、{@link android.content.ContentProviderOperation} オブジェクトの {@link java.util.ArrayList} を作成し、{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} を呼び出します。 連絡先プロバイダはすべての操作を 1 つのトランザクションの 1 つの {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} で行うため、変更内容が連絡先リポジトリで不整合のままになることは決してありません。 また、バッチ変更では、未加工連絡先とその詳細データを同時に挿入しやすくなっています。

注: 1 つの未加工連絡先を変更する場合は、変更を独自アプリ内で処理するのではなく、インテントを端末の連絡先アプリケーションに送信することを検討してください。この方法については、インテントを使用した取得と変更で詳しく説明しています。

明け渡し点

多数の操作を伴うバッチ変更は、他のプロセスをブロックしてユーザーにとっての全体的な使用感を悪化させかねません。 意図したすべての変更をできるだけ少ない個別リストに整理するとともに、それらがシステムをブロックしないようにするために、1 つまたは複数の操作に明け渡し点を設定してください。 明け渡し点の実体は、{@link android.content.ContentProviderOperation#isYieldAllowed()} の値が true に設定された {@link android.content.ContentProviderOperation} オブジェクトです。 連絡先プロバイダが明け渡し点に遭遇すると、作業を一時中断して他のプロセスを実行させ、現在のトランザクションをクローズします。 作業を再開した連絡先プロバイダは、{@link java.util.ArrayList} に含まれている次の操作を行い、新しいトランザクションを始めます。

明け渡し点により、{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} の呼び出し 1 回につき複数のトランザクションが発生します。 そのため、明け渡し点は 1 組の関連する行への最後の操作に設定してください。 たとえば、未加工連絡先の行とその関連データ行を追加する一連の操作の最後に、あるいは 1 人の連絡先に関連する 1 組の行に対する最後の操作に、明け渡し点を設定します。

明け渡し点は、アトミック操作の単位でもあります。2 つの明け渡し点間のすべてのアクセスは、1 つのまとまりとして成功または失敗します。 明け渡し点を設定しない場合、最小のアトミック操作は操作のバッチ全体になります。 明け渡し点を使用すると、操作がシステムのパフォーマンスを低下させるのを防ぐと同時に、操作の一部分が確実にアトミックになります。

変更の後方参照

新しい未加工連絡先の行とその関連データの行を 1 組の {@link android.content.ContentProviderOperation} オブジェクトとして挿入している場合は、データの行を未加工連絡先の行にリンクする必要があります。そのためには、未加工連絡先の {@code android.provider.BaseColumns#_ID} 値を {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} 値として挿入します。 ただし、この値は、データの行のために{@link android.content.ContentProviderOperation} を作成している間は使用できません。未加工連絡先に {@link android.content.ContentProviderOperation} 操作がまだ適用されていないからです。 これを回避するため、{@link android.content.ContentProviderOperation.Builder} クラスにはメソッド {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} が用意されています。 このメソッドを使用すると、前の操作の結果を使用して列を挿入または変更できます。

{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} メソッドには引数が 2 つあります。

key
キーと値のペアのキーです。この引数の値は、変更中のテーブルに含まれる列であることが必要です。
previousResult
{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} から取得した {@link android.content.ContentProviderResult} オブジェクトの配列に含まれる値の、0 ベースのインデックスです。 バッチ操作が適用されると、各操作の結果は結果用の中間配列に格納されます。 previousResult 値はそうした結果の 1 つのインデックスで、key 値を使用して取得され、格納されます。 これにより、新しい未加工連絡先レコードを挿入してその {@code android.provider.BaseColumns#_ID} 値に戻り、次に {@link android.provider.ContactsContract.Data} 行を追加するときにその値を「後方参照」できます。

{@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} を初めて呼び出すと、指定した {@link android.content.ContentProviderOperation} オブジェクトの {@link java.util.ArrayList} と同じサイズを使用して、結果配列全体が作成されます。 ただし、結果配列に含まれるすべての要素は null に設定され、そのためまだ適用されていない操作から結果を後方参照しようとすると {@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} が {@link java.lang.Exception} をスローします。

次のスニペットは、新しい未加工連絡先とデータをバッチで挿入する方法を示しています。その中には、明け渡し点を指定し、後方参照を使用するコードが含まれています。 これらのスニペットは、 Contact Manager サンプル アプリケーションの ContactAdder クラスの一部である createContacEntry() メソッドの拡張版です。

最初のスニペットは、連絡先データを UI から取得します。この時点で、ユーザーは新しい未加工連絡先の追加先となるアカウントを既に選択してあります。

// Creates a contact entry from the current UI values, using the currently-selected account.
protected void createContactEntry() {
    /*
     * Gets values from the UI
     */
    String name = mContactNameEditText.getText().toString();
    String phone = mContactPhoneEditText.getText().toString();
    String email = mContactEmailEditText.getText().toString();

    int phoneType = mContactPhoneTypes.get(
            mContactPhoneTypeSpinner.getSelectedItemPosition());

    int emailType = mContactEmailTypes.get(
            mContactEmailTypeSpinner.getSelectedItemPosition());

次のスニペットは、未加工連絡先の行を {@link android.provider.ContactsContract.RawContacts} テーブルに挿入する操作を作成します。

    /*
     * Prepares the batch operation for inserting a new raw contact and its data. Even if
     * the Contacts Provider does not have any data for this person, you can't add a Contact,
     * only a raw contact. The Contacts Provider will then add a Contact automatically.
     */

     // Creates a new array of ContentProviderOperation objects.
    ArrayList<ContentProviderOperation> ops =
            new ArrayList<ContentProviderOperation>();

    /*
     * Creates a new raw contact with its account type (server type) and account name
     * (user's account). Remember that the display name is not stored in this row, but in a
     * StructuredName data row. No other data is required.
     */
    ContentProviderOperation.Builder op =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
            .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

次に、表示名、電話、メールのデータ行を作成します。

操作の各ビルダー オブジェクトは、{@link android.content.ContentProviderOperation.Builder#withValueBackReference(String, int) withValueBackReference()} を使用して {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} を取得します。 この参照が最初の操作からの {@link android.content.ContentProviderResult} オブジェクトを後方参照しており、それが未加工連絡先の行を追加し、新しい {@code android.provider.BaseColumns#_ID} 値を返します。 その結果、各データ行はその {@link android.provider.ContactsContract.DataColumns#RAW_CONTACT_ID} によって、属する新しい {@link android.provider.ContactsContract.RawContacts} 行に自動的にリンクされます。

メール行を追加する {@link android.content.ContentProviderOperation.Builder} オブジェクトは、明け渡し点を設定する {@link android.content.ContentProviderOperation.Builder#withYieldAllowed(boolean) withYieldAllowed()} でフラグ付けされます。

    // Creates the display name for the new raw contact, as a StructuredName data row.
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * withValueBackReference sets the value of the first argument to the value of
             * the ContentProviderResult indexed by the second argument. In this particular
             * call, the raw contact ID column of the StructuredName data row is set to the
             * value of the result returned by the first operation, which is the one that
             * actually adds the raw contact row.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to StructuredName
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)

            // Sets the data row's display name to the name in the UI.
            .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified phone number and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Phone
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

            // Sets the phone number and type
            .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
            .withValue(ContactsContract.CommonDataKinds.Phone.TYPE, phoneType);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

    // Inserts the specified email and type as a Phone data row
    op =
            ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
            /*
             * Sets the value of the raw contact id column to the new raw contact ID returned
             * by the first operation in the batch.
             */
            .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)

            // Sets the data row's MIME type to Email
            .withValue(ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

            // Sets the email address and type
            .withValue(ContactsContract.CommonDataKinds.Email.ADDRESS, email)
            .withValue(ContactsContract.CommonDataKinds.Email.TYPE, emailType);

    /*
     * Demonstrates a yield point. At the end of this insert, the batch operation's thread
     * will yield priority to other threads. Use after every set of operations that affect a
     * single contact, to avoid degrading performance.
     */
    op.withYieldAllowed(true);

    // Builds the operation and adds it to the array of operations
    ops.add(op.build());

最後のスニペットは、新しい未加工連絡先とデータ行を挿入する {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} を呼び出しています。

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG,"Selected account: " + mSelectedAccount.getName() + " (" +
            mSelectedAccount.getType() + ")");
    Log.d(TAG,"Creating contact: " + name);

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {

            getContentResolver().applyBatch(ContactsContract.AUTHORITY, ops);
    } catch (Exception e) {

            // Display a warning
            Context ctx = getApplicationContext();

            CharSequence txt = getString(R.string.contactCreationFailure);
            int duration = Toast.LENGTH_SHORT;
            Toast toast = Toast.makeText(ctx, txt, duration);
            toast.show();

            // Log exception
            Log.e(TAG, "Exception encountered while inserting contact: " + e);
    }
}

バッチ処理を使用すると、楽観的並行性制御を実装できます。これは、背後のリポジトリをロックする必要なく変更トランザクションを適用する手法です。 この手法を使用するには、トランザクションを適用してから、同時に行うことのできる可能性のある他の変更をチェックします。 一貫性のない変更が発生していることがわかった場合は、トランザクションをロールバックしてやり直します。

楽観的並行性制御は、一度に使用するユーザーが 1 人でデータリポジトリへの同時アクセスがまれなモバイル端末に便利です。 ロックが使用されないため、ロックの設定や他のトランザクションによるロックの解放待ちで時間を無駄にしません。

{@link android.provider.ContactsContract.RawContacts} 行を 1 行更新している間に楽観的並行性制御を使用する方法は次のとおりです。

  1. 取得する他のデータとともに、未加工連絡先の {@link android.provider.ContactsContract.SyncColumns#VERSION} 列を取得します。
  2. 制約を強制するのに適した {@link android.content.ContentProviderOperation.Builder} オブジェクトを、メソッド {@link android.content.ContentProviderOperation#newAssertQuery(Uri)} を使用して作成します。 コンテンツ URI に、未加工連絡先の {@code android.provider.BaseColumns#_ID} が末尾に追加された {@link android.provider.ContactsContract.RawContacts#CONTENT_URI RawContacts.CONTENT_URI} を使用します。
  3. {@link android.content.ContentProviderOperation.Builder} オブジェクトに対し、{@link android.content.ContentProviderOperation.Builder#withValue(String, Object) withValue()} を呼び出して、{@link android.provider.ContactsContract.SyncColumns#VERSION} 列と取得したばかりのバージョン番号を比較します。
  4. 同じ {@link android.content.ContentProviderOperation.Builder} に対し、{@link android.content.ContentProviderOperation.Builder#withExpectedCount(int) withExpectedCount()} を呼び出して、このアサーションで 1 行だけがテストされるようにします。
  5. {@link android.content.ContentProviderOperation.Builder#build()} を呼び出して {@link android.content.ContentProviderOperation} オブジェクトを作成し、次のこのオブジェクトを {@link android.content.ContentResolver#applyBatch(String, ArrayList) applyBatch()} に渡す {@link java.util.ArrayList} に先頭オブジェクトとして追加します。
  6. バッチトランザクションを適用します。

目的の未加工連絡先の行が、行の読み込みからその変更の試行までの間に別の操作によってアップデートされた場合、{@link android.content.ContentProviderOperation} の「アサート」が失敗し、操作のバッチ全体がバックアウトされます。 バックアウトが終わったら、バッチをやり直すこと、または他のアクションを行うことができます。

次のスニペットは、{@link android.content.CursorLoader} を使用して未加工連絡先を 1 つクエリした後に、{@link android.content.ContentProviderOperation} を「アサート」します。

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {

    // Gets the raw contact's _ID and VERSION values
    mRawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION));
}

...

// Sets up a Uri for the assert operation
Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, mRawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.netAssertQuery(rawContactUri);

// Adds the assertions to the assert operation: checks the version and count of rows tested
assertOp.withValue(SyncColumns.VERSION, mVersion);
assertOp.withExpectedCount(1);

// Creates an ArrayList to hold the ContentProviderOperation objects
ArrayList ops = new ArrayList<ContentProviderOperationg>;

ops.add(assertOp.build());

// You would add the rest of your batch operations to "ops" here

...

// Applies the batch. If the assert fails, an Exception is thrown
try
    {
        ContentProviderResult[] results =
                getContentResolver().applyBatch(AUTHORITY, ops);

    } catch (OperationApplicationException e) {

        // Actions you want to take if the assert operation fails go here
    }

インテントを使用した取得と変更

インテントを端末の連絡先アプリケーションに送信すると、連絡先プロバイダに間接的にアクセスできます。 インテントが端末の連絡先アプリケーション UI を起動し、ユーザーはそこで連絡先関連の作業を行えます。 このタイプのアクセスで、ユーザーは次の作業ができます。

ユーザーがデータを挿入またはアップデートしている場合は、まずデータを収集し、次にそれをインテントの一部として送信できます。

インテントを使用して端末の連絡先アプリケーション経由で連絡先プロバイダにアクセスする場合、連絡先プロバイダにアクセスするための独自の UI やコードを作成する必要はありません。 また、連絡先プロバイダに対する読み取りや書き込みのパーミッションを要求する必要もありません。 端末の連絡先アプリケーションは、連絡先に対する読み取りパーミッションをデリゲートできます。また、連絡先プロバイダへの変更を他のアプリケーション経由で行っていることから、書き込みパーミッションは不要です。

プロバイダにアクセスするためのインテントを送信する汎用プロセスについては、コンテンツ プロバイダの基本のセクション「インテント経由のデータアクセス」で詳しく説明しています。 表 4 に、利用可能なタスクで使用できるアクション、MIME タイプ、データ値をまとめます。{@link android.content.Intent#putExtra(String, String) putExtra()} で使用できるエクストラ値については、{@link android.provider.ContactsContract.Intents.Insert} のリファレンス ドキュメントに一覧があります。

表 4. 連絡先プロバイダのインテント

タスク アクション データ MIME タイプ
連絡先をリストから選ぶ {@link android.content.Intent#ACTION_PICK} 次のどれかです。
  • {@link android.provider.ContactsContract.Contacts#CONTENT_URI Contacts.CONTENT_URI}。連絡先のリストを表示します。
  • {@link android.provider.ContactsContract.CommonDataKinds.Phone#CONTENT_URI Phone.CONTENT_URI}。未加工連絡先の電話番号のリストを表示します。
  • {@link android.provider.ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_URI StructuredPostal.CONTENT_URI}。未加工連絡先の住所のリストを表示します。
  • {@link android.provider.ContactsContract.CommonDataKinds.Email#CONTENT_URI Email.CONTENT_URI}。未加工連絡先のメールアドレスのリストを表示します。
使用せず 指定したコンテンツ URI のタイプに応じて、未加工連絡先のリストか、未加工連絡先から取得されたデータのリストを表示します。

{@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()} を呼び出してください。それにより、選択した行のコンテンツ URI が返されます。 URI の形式は、テーブルのコンテンツ URI の末尾にその行の LOOKUP_ID が追加されたものです。 端末の連絡先アプリは、このアクティビティの実行中を通して、読み取りと書き込みのパーミッションをこのコンテンツ URI にデリゲートします。 詳しくは、「コンテンツ プロバイダの基本」をご覧ください。

新しい未加工連絡先を挿入する {@link android.provider.ContactsContract.Intents.Insert#ACTION Insert.ACTION} 該当せず {@link android.provider.ContactsContract.RawContacts#CONTENT_TYPE RawContacts.CONTENT_TYPE}。未加工連絡先のセットに対する MIME タイプです。 端末の連絡先アプリケーションの [連絡先の追加] 画面を表示します。インテントに追加したエクストラ値が表示されます。 {@link android.app.Activity#startActivityForResult(Intent, int) startActivityForResult()} を使用して送信すると、新たに追加された未加工連絡先の URI が、アクティビティの {@link android.app.Activity#onActivityResult(int, int, Intent) onActivityResult()} コールバック メソッドの {@link android.content.Intent} 引数の「data」フィールドに格納されて戻ってきます。 この値を取得するには、{@link android.content.Intent#getData()} を呼び出します。
連絡先を編集する {@link android.content.Intent#ACTION_EDIT} 連絡先の {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}。 エディタ アクティビティを使用すると、ユーザーがその連絡先に関連付けられている任意のデータを編集できます。 {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE Contacts.CONTENT_ITEM_TYPE}。1 つの連絡先です。 連絡先アプリケーションに連絡先の編集画面を表示します。インテントに追加したエクストラ値が表示されます。 ユーザーが [完了] をタップして編集内容を保存すると、アクティビティがフォアグラウンドに戻ります。
ピッカー(やはりデータを追加できる)を表示する。 {@link android.content.Intent#ACTION_INSERT_OR_EDIT} 該当せず {@link android.provider.ContactsContract.Contacts#CONTENT_ITEM_TYPE} このインテントは、連絡先アプリのピッカー画面を常に表示します。ユーザーは、編集する連絡先を選ぶか、新しい連絡先を追加できます。 ユーザーの選択に応じて編集画面か追加画面が開き、インテントに含めて渡したエクストラ データが表示されます。 メールアドレスや電話番号などの連絡先データを独自アプリに表示する場合は、このインテントを使用すると、ユーザーが既存の連絡先にデータを追加できます。

注: このインテントのエクストラで名前の値を送信する必要はありません。ユーザーは必ず既存の名前を選ぶか新しい名前を追加するからです。 そのうえ、アプリが名前を送信し、ユーザーが編集することを選んだ場合、連絡先アプリは送信した名前を表示し、以前の値を上書きします。 ユーザーがこのことに気付かず、編集内容を保存すると、以前の値が失われます。

端末の連絡先アプリは、インテントを使用して未加工連絡先や任意のデータを削除することを許可しません。 そのため、未加工連絡先を削除するには {@link android.content.ContentResolver#delete(Uri, String, String[]) ContentResolver.delete()} または {@link android.content.ContentProviderOperation#newDelete(Uri) ContentProviderOperation.newDelete()} を使用してください。

次のスニペットは、新しい未加工連絡先とデータをバッチで挿入するインテントをコンストラクトして送信する方法を示しています。

// Gets values from the UI
String name = mContactNameEditText.getText().toString();
String phone = mContactPhoneEditText.getText().toString();
String email = mContactEmailEditText.getText().toString();

String company = mCompanyName.getText().toString();
String jobtitle = mJobTitle.getText().toString();

// Creates a new intent for sending to the device's contacts application
Intent insertIntent = new Intent(ContactsContract.Intents.Insert.ACTION);

// Sets the MIME type to the one expected by the insertion activity
insertIntent.setType(ContactsContract.RawContacts.CONTENT_TYPE);

// Sets the new contact name
insertIntent.putExtra(ContactsContract.Intents.Insert.NAME, name);

// Sets the new company and job title
insertIntent.putExtra(ContactsContract.Intents.Insert.COMPANY, company);
insertIntent.putExtra(ContactsContract.Intents.Insert.JOB_TITLE, jobtitle);

/*
 * Demonstrates adding data rows as an array list associated with the DATA key
 */

// Defines an array list to contain the ContentValues objects for each row
ArrayList<ContentValues> contactData = new ArrayList<ContentValues>();


/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
ContentValues rawContactRow = new ContentValues();

// Adds the account type and name to the row
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_TYPE, mSelectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, mSelectedAccount.getName());

// Adds the row to the array
contactData.add(rawContactRow);

/*
 * Sets up the phone number data row
 */

// Sets up the row as a ContentValues object
ContentValues phoneRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
phoneRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
);

// Adds the phone number and its type to the row
phoneRow.put(ContactsContract.CommonDataKinds.Phone.NUMBER, phone);

// Adds the row to the array
contactData.add(phoneRow);

/*
 * Sets up the email data row
 */

// Sets up the row as a ContentValues object
ContentValues emailRow = new ContentValues();

// Specifies the MIME type for this data row (all data rows must be marked by their type)
emailRow.put(
        ContactsContract.Data.MIMETYPE,
        ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
);

// Adds the email address and its type to the row
emailRow.put(ContactsContract.CommonDataKinds.Email.ADDRESS, email);

// Adds the row to the array
contactData.add(emailRow);

/*
 * Adds the array to the intent's extras. It must be a parcelable object in order to
 * travel between processes. The device's contacts app expects its key to be
 * Intents.Insert.DATA
 */
insertIntent.putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData);

// Send out the intent to start the device's contacts app in its add contact activity.
startActivity(insertIntent);

データの整合性

連絡先リポジトリにはユーザーによる修正や更新が予想される重要で秘密性の高いデータが格納されるため、連絡先プロバイダにはデータの整合性に関して明確に定義されたルールがあります。 連絡先データを変更する際にこれらのルールを守ることは、開発側の責任です。 ここでは、重要なルールを示します。

{@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 行を、追加する {@link android.provider.ContactsContract.RawContacts} 行ごとに必ず追加してください。
{@link android.provider.ContactsContract.Data} テーブルに {@link android.provider.ContactsContract.CommonDataKinds.StructuredName} 行のない {@link android.provider.ContactsContract.RawContacts} 行は、集約中に問題を引き起こすことがあります。
新しい {@link android.provider.ContactsContract.Data} 行を、その親 {@link android.provider.ContactsContract.RawContacts} 行に必ずリンクしてください。
{@link android.provider.ContactsContract.RawContacts} にリンクされていない {@link android.provider.ContactsContract.Data} 行は端末の連絡先アプリケーションで可視にならず、同期アダプタで問題になることがあります。
変更するのは、所有する未加工連絡先のデータに限ってください。
通常、連絡先プロバイダは複数の異なるアカウント タイプやオンライン サービスから取得したデータを管理しています。 そのため、アプリケーションが所有しているデータのみ変更または削除されるようにし、アプリケーションが管理しているアカウント タイプとアカウント名のデータのみ挿入するようにすることが必要です。
権限、コンテンツ URI、URI パス、列名、MIME タイプ、{@link android.provider.ContactsContract.CommonDataKinds.CommonColumns#TYPE} 値については、{@link android.provider.ContactsContract} とそのサブクラスに定義されている定数を必ず使用してください。
そのような定数を使用することがエラーの予防に役立ちます。また、使われなくなった定数があれば、コンパイラーから警告として通知されます。

カスタムデータ行

独自のカスタム MIME タイプを作成して使用すると、{@link android.provider.ContactsContract.Data} テーブルで独自データ行を挿入、編集、削除、取得できます。 独自行で使用できるのは {@link android.provider.ContactsContract.DataColumns} に定義された列だけですが、独自タイプに固有の列名をデフォルトの列名にマッピングできます。 端末の連絡先アプリケーションで、独自行のデータは表示されますが、編集または削除はできず、ユーザーはデータの追加ができません。 ユーザーがカスタムデータ行を変更できるようにするには、独自のアプリケーションでエディタ アクティビティを提供する必要があります。

カスタムデータを表示するには、1 つの <ContactsAccountType> 要素と 1 つまたは複数の <ContactsDataKind> 子要素を含む contacts.xml ファイルを提供します。 詳しくは、<ContactsDataKind> elementに関するセクションで説明しています。

カスタム MIME タイプについて詳しくは、「コンテンツ プロバイダの作成」をご覧ください。

連絡先プロバイダの同期アダプタ

連絡先プロバイダは、特に端末とオンライン サービスとの間における連絡先データの同期を処理するために設計されています。 これを使用することで、ユーザーは既存のデータを新しい端末にダウンロードしたり、既存のデータを新しいアカウントにアップロードしたりできています。 また、同期によって、追加や変更のソースに関係なく、ユーザーは最新データを入手できます。 同期の利点としては他にも、端末がネットワークに接続されていなくても連絡先データを使用できるようになることが挙げられます。

同期はさまざまな手法で実装できますが、Android システムでは次のタスクを自動化するプラグイン同期フレームワークを提供しています。

このフレームワークを使用するには、同期アダプタ プラグインを提供する必要があります。各同期アダプタはサービスと連絡先プロバイダに対して一意ですが、同じサービスに対して複数のアカウント名を処理できます。 また、このフレームワークでは同じサービスとプロバイダに対して複数の同期アダプタが可能です。

同期アダプタのクラスとファイル

同期アダプタは、{@link android.content.AbstractThreadedSyncAdapter} のサブクラスとして実装し、Android アプリケーションの一部としてインストールします。 システムは同期アダプタに関する知識を、アプリケーション マニフェストの要素からと、マニフェストが指す専用の XML ファイルから取得します。 この XML ファイルは、オンライン サービスのアカウント タイプと連絡先プロバイダに絡む権限を定義しており、この組み合わせでアダプタを一意に識別します。 同期アダプタは、ユーザーが同期アダプタのアカウント タイプに対してアカウントを追加し、同期アダプタの同期対象である連絡先プロバイダで同期を有効にすることで、アクティブになります。 この時点で、システムはアダプタの管理を開始し、連絡先プロバイダとサーバーとの同期が必要になるとそれを呼び出します。

注: アカウント タイプを同期アダプタの識別の一環として使用することにより、システムは同じ組織から異なるサービスにアクセスする同期アダプタを検出してグループ化できます。 たとえば、Google オンライン サービス用の同期アダプタでは、すべて同じアカウント タイプ com.google です。 ユーザーが Google アカウントを端末に追加すると、Google サービス用にインストールされているすべての同期アダプタがひとまとめにリストされ、リストされている各同期アダプタは端末上の異なる連絡先プロバイダと同期します。

データにアクセスするたび、ほとんどのサービスがユーザーに ID の確認を要求するため、Android システムでは、同期アダプタ フレームワークと類似した認証フレームワークを提供しており、往々にして同期アダプタ フレームワークと組み合わせて使用します。 この認証フレームワークでは、{@link android.accounts.AbstractAccountAuthenticator} のサブクラスであるプラグイン認証システムを使用します。 認証システムは、次の手順でユーザーの身元を検証します。

  1. ユーザー名とパスワードか、それに準ずる情報(ユーザーの資格情報)を収集します。
  2. 収集した資格情報をサービスに送信します。
  3. サービスの返答を検証します。

サービスが資格情報を受け入れた場合、認証システムはその資格情報を後での使用のために格納します。 {@link android.accounts.AccountManager} はプラグイン認証フレームワークのおかげで、OAuth2 認証トークンなど、認証システムがサポートして公開することを選択している任意の認証トークンへのアクセスを提供できます。

認証は必須ではありませんが、たいていの連絡先サービスが使用しています。 ただし、認証に Android 認証フレームワークを使用する必要はありません。

同期アダプタの実装

連絡先プロバイダ用の同期アダプタを実装するには、まず以下を備えた Android アプリケーションを作成します。

システムからの同期アダプタとのバインド要求に対処する {@link android.app.Service} コンポーネント。
システムは、同期しようとするとき、サービスの {@link android.app.Service#onBind(Intent) onBind()} メソッドを呼び出して、同期アダプタの {@link android.os.IBinder} を取得します。 これにより、システムはアダプタのメソッドに対するプロセス間呼び出しを実行できます。

サンプル同期アダプタのサンプルアプリで、このサービスのクラス名は com.example.android.samplesync.syncadapter.SyncService です。

{@link android.content.AbstractThreadedSyncAdapter} の具象サブクラスとして実装された、実際の同期アダプタ。
このクラスが、サーバーからのデータのダウンロード、端末からのデータのアップロード、競合の解消といった作業を実施します。 アダフタの主な作業は、メソッド {@link android.content.AbstractThreadedSyncAdapter#onPerformSync( Account, Bundle, String, ContentProviderClient, SyncResult) onPerformSync()} で実行されます。 このクラスは、シングルトンとしてインスタンス化する必要があります。

サンプル同期アダプタのサンプルアプリで、同期アダプタはクラス com.example.android.samplesync.syncadapter.SyncAdapter に定義されています。

{@link android.app.Application} のサブクラス。
このクラスは同期アダプタ シングルトンのファクトリとして機能します。同期アダプタのインスタンス化には {@link android.app.Application#onCreate()} メソッドを使用し、同期アダプタ シングルトンを同期アダプタのサービスの {@link android.app.Service#onBind(Intent) onBind()} メソッドに返すための静的な「getter」メソッドを提供します。
省略可能: システムからのユーザー認証要求に対処する {@link android.app.Service} コンポーネント。
{@link android.accounts.AccountManager} は、このサービスを開始することによって認証プロセスを始めます。 サービスの {@link android.app.Service#onCreate()} メソッドは、認証システム オブジェクトをインスタンス化します。 システムは、アプリケーションの同期アダプタのためにユーザー アカウントを認証しようとするとき、サービスの {@link android.app.Service#onBind(Intent) onBind()} メソッドを呼び出して、認証システムの {@link android.os.IBinder} を取得します。 これにより、システムは認証システムのメソッドに対するプロセス間呼び出しを実行できます。

サンプル同期アダプタのサンプルアプリで、このサービスのクラス名は com.example.android.samplesync.authenticator.AuthenticationService です。

省略可能: 認証の要求を処理する {@link android.accounts.AbstractAccountAuthenticator} の具象サブクラス。
このクラスは、ユーザーの資格情報をサーバーに対して認証するために {@link android.accounts.AccountManager} が呼び出すメソッドを提供します。認証プロセスの詳細は、使用されているサーバー技術に応じて多岐にわたります。 認証について詳しくは、使用するサーバー ソフトウェアのドキュメントをご覧ください。

サンプル同期アダプタのサンプルアプリで、認証はクラス com.example.android.samplesync.authenticator.Authenticator に定義されています。

システムに対する同期アダプタと認証システムを定義する XML ファイル。
前述の同期アダプタと認証システム サービスのコンポーネントは、アプリケーション マニフェストの <service> 要素に定義されます。 これらの要素には、特定のデータをシステムに提供する次のような <meta-data> 子要素が含まれます。

ソーシャル ストリーム データ

{@code android.provider.ContactsContract.StreamItems} テーブルと {@code android.provider.ContactsContract.StreamItemPhotos} テーブルは、ソーシャル ネットワークからの受信データを管理します。 独自ネットワークからのストリーム データをこれらのテーブルに追加する同期アダプタを作成したり、ストリーム データをこれらのテーブルから読み込んで独自アプリケーションに表示したり、この両方を行ったりできます。 このような機能があると、ソーシャル ネットワーキングのサービスやアプリケーションを Android のソーシャル ネットワーキング機能に統合できます。

ソーシャル ストリーム テキスト

ストリーム アイテムは、必ず未加工連絡先に関連付けられます。{@code android.provider.ContactsContract.StreamItemsColumns#RAW_CONTACT_ID} は、未加工連絡先の _ID 値にリンクされます。 未加工連絡先のアカウント タイプとアカウント名は、ストリーム アイテム行にも格納されます。

ストリームからのデータを次の列に格納します。

{@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_TYPE}
必須。このストリーム アイテムに関連付けられている未加工連絡先の、ユーザーのアカウント タイプ。 ストリーム アイテムを挿入する際には、この値を忘れずに設定してください。
{@code android.provider.ContactsContract.StreamItemsColumns#ACCOUNT_NAME}
必須。このストリーム アイテムに関連付けられている未加工連絡先の、ユーザーのアカウント名。 ストリーム アイテムを挿入する際には、この値を忘れずに設定してください。
識別用列
必須。ストリーム アイテムを挿入する際には、次の識別用列を挿入する必要があります。
{@code android.provider.ContactsContract.StreamItemsColumns#COMMENTS}
省略可能。ストリーム アイテムの冒頭に表示できる概要情報を格納します。
{@code android.provider.ContactsContract.StreamItemsColumns#TEXT}
ストリーム アイテムのテキスト。アイテムのソースによって投稿されたコンテンツか、そのストリーム アイテムを生成した何らかのアクションに関する説明です。 この列には、{@link android.text.Html#fromHtml(String) fromHtml()} でレンダリングできる任意の書式や埋め込みリソース画像を含めることができます。 プロバイダは、長いコンテンツを切り捨てたり省いたりすることがありますが、タグは壊さないようにします。
{@code android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP}
ストリーム アイテムが挿入またはアップデートされた時刻を含むテキスト。エポックからのミリ秒の形式です。 ストリーム アイテムを挿入またはアップデートするアプリケーションは、この列の管理を担当します。この列は、連絡先プロバイダによって自動的に管理されるわけではありません。

ストリーム アイテムの識別情報を表示するには、{@code android.provider.ContactsContract.StreamItemsColumns#RES_ICON}、{@code android.provider.ContactsContract.StreamItemsColumns#RES_LABEL}、{@code android.provider.ContactsContract.StreamItemsColumns#RES_PACKAGE} を使用してアプリケーション内のリソースにリンクします。

{@code android.provider.ContactsContract.StreamItems} テーブルには、同期アダプタ専用に列 {@code android.provider.ContactsContract.StreamItemsColumns#SYNC1} ~ {@code android.provider.ContactsContract.StreamItemsColumns#SYNC4} も格納されます。

ソーシャル ストリーム フォト

{@code android.provider.ContactsContract.StreamItemPhotos} テーブルには、ストリーム アイテムに関連付けられた写真が格納されます。 このテーブルの {@code android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID} 列は、{@code android.provider.ContactsContract.StreamItems} テーブルの {@code android.provider.BaseColumns#_ID} 列の値にリンクされます。 写真の参照は、テーブルの次の列に格納されます。

{@code android.provider.ContactsContract.StreamItemPhotos#PHOTO} 列(BLOB)。
写真のバイナリ表現。格納や表示のためにプロバイダによってサイズが変更されます。 この列は、写真を格納するために使用されていた連絡先プロバイダの従来のバージョンとの下方互換性のために用意されています。 ただし、現在のバージョンでは、写真の格納にこの列を使用しないでください。 代わりに、{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID} または {@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI}(どちらについても次の項目で説明します)を使用して、写真をファイルに保存します。 現状では、この列には写真のサムネイルが格納されており、読み出し可能です。
{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_FILE_ID}
未加工連絡先の写真の数値 ID。この値を定数 {@link android.provider.ContactsContract.DisplayPhoto#CONTENT_URI DisplayPhoto.CONTENT_URI} の末尾に追加して、1 つの写真ファイルを指すコンテンツ URI を取得し、{@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) openAssetFileDescriptor()} を呼び出して、その写真ファイルのハンドルを取得します。
{@code android.provider.ContactsContract.StreamItemPhotosColumns#PHOTO_URI}
この行によって表されている写真の写真ファイルを直接指すコンテンツ URI。 この URI を使用して {@link android.content.ContentResolver#openAssetFileDescriptor(Uri, String) openAssetFileDescriptor()} を呼び出して、写真ファイルのハンドルを取得します。

ソーシャル ストリーム テーブルの使用

これらのテーブルは、連絡先プロバイダの他の主なテーブルと同じように機能しますが、次のような例外があります。

クラス {@code android.provider.ContactsContract.StreamItems.StreamItemPhotos} は、1 つのストリーム アイテムの写真行を含む {@code android.provider.ContactsContract.StreamItemPhotos} のサブテーブルを定義します。

ソーシャル ストリーム操作

連絡先プロバイダによって端末の連絡先アプリケーションと連携して管理されるソーシャル ストリーム データは、ソーシャル ネットワーキング システムと既存の連絡先を接続するためのとても便利な手段を用意しています。 利用できる機能は次のとおりです。

連絡先プロバイダを使用したストリーム アイテムの定期的な同期は、他の同期と同じです。 同期について詳しくは、連絡先プロバイダの同期アダプタをご覧ください。 通知の登録と連絡先の招待については、次の 2 つのセクションで説明します。

ソーシャル ネットワーキング表示を処理するための登録

同期アダプタを登録し、同期アダプタによって管理されている連絡先をユーザーが表示したときに通知されるようにする方法は、次のとおりです。

  1. contacts.xml という名前のファイルをプロジェクトの res/xml/ ディレクトリに作成します。 このファイルが既に存在する場合は、この手順を省略できます。
  2. このファイルに要素 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> を追加します。 この要素が既に存在する場合は、この手順を省略できます。
  3. ユーザーが端末の連絡先アプリケーションで連絡先の詳細ページを開いたときに通知されるサービスを登録するには、属性 viewContactNotifyService="serviceclass" を要素に追加します。serviceclass は、端末の連絡先アプリケーションからインテントを受け取ることになるサービスの完全修飾クラス名です。 通知側サービスのために、{@link android.app.IntentService} を機能拡張したクラスを使用して、そのサービスがインテントを受け取れるようにします。 受け取るインテントに含まれるデータには、ユーザーがクリックした未加工連絡先のコンテンツ URI が格納されます。 通知側サービスから、同期アダプタをバインドして呼び出し、未加工連絡先のデータをアップデートできます。

ユーザーがストリーム アイテムかストリーム フォトかその両方をクリックしたときに呼び出されるアクティビティを登録する方法は次のとおりです。

  1. contacts.xml という名前のファイルをプロジェクトの res/xml/ ディレクトリに作成します。 このファイルが既に存在する場合は、この手順を省略できます。
  2. このファイルに要素 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> を追加します。 この要素が既に存在する場合は、この手順を省略できます。
  3. アクティビティのどれかを登録して、ユーザーが端末の連絡先アプリケーションでストリーム アイテムをタップしたことに対処するには、属性 viewStreamItemActivity="activityclass" を要素に追加します。activityclass は、端末の連絡先アプリケーションからインテントを受け取ることになるアクティビティの完全修飾クラス名です。
  4. アクティビティのどれかを登録して、ユーザーが端末の連絡先アプリケーションでストリーム フォトをタップしたことに対処するには、属性 viewStreamItemPhotoActivity="activityclass" を要素に追加します。activityclass は、端末の連絡先アプリケーションからインテントを受け取ることになるアクティビティの完全修飾クラス名です。

<ContactsAccountType> 要素については、<ContactsAccountType> 要素で詳しく説明しています。

受け取るインテントには、ユーザーがクリックしたアイテムまたは写真のコンテンツ URI が格納されます。 テキスト アイテムと写真とで異なるアクティビティを使用するには、同じファイルで両方の属性を使用します。

ソーシャル ネットワーキング サービスの操作

ユーザーは、連絡先をソーシャル ネットワーキング サービスに招待するのに、端末の連絡先アプリケーションを離れる必要はありません。 その代わりとして、連絡先をアクティビティのどれかに招待するインテントを端末の連絡先アプリケーションに送信させることができます。 これをセットアップする方法は次のとおりです。

  1. contacts.xml という名前のファイルをプロジェクトの res/xml/ ディレクトリに作成します。 このファイルが既に存在する場合は、この手順を省略できます。
  2. このファイルに要素 <ContactsAccountType xmlns:android="http://schemas.android.com/apk/res/android"> を追加します。 この要素が既に存在する場合は、この手順を省略できます。
  3. 次の属性を追加します。 activityclass 値は、インテントを受信するアクティビティの完全修飾クラス名です。 invite_action_label 値は、端末の連絡先アプリケーションの [Add Connection] メニューに表示される文字列です。

注: ContactsSource は廃止されたタグ名で、ContactsAccountType に置き換わっています。

contacts.xml リファレンス

ファイル contacts.xml には、同期アダプタやアプリケーションと連絡先アプリケーションや連絡先プロバイダとのやり取りを管理する XML 要素が含まれています。 これらの要素について、以降のセクションで説明します。

<ContactsAccountType> 要素

<ContactsAccountType> 要素は、アプリケーションと連絡先アプリケーションとのやり取りを管理します。 構文は次のとおりです。

<ContactsAccountType
        xmlns:android="http://schemas.android.com/apk/res/android"
        inviteContactActivity="activity_name"
        inviteContactActionLabel="invite_command_text"
        viewContactNotifyService="view_notify_service"
        viewGroupActivity="group_view_activity"
        viewGroupActionLabel="group_action_text"
        viewStreamItemActivity="viewstream_activity_name"
        viewStreamItemPhotoActivity="viewphotostream_activity_name">

含まれているファイル:

res/xml/contacts.xml

含めることのできる要素:

<ContactsDataKind>

説明:

ユーザーが連絡先の 1 人をソーシャル ネットワーキングに招待できるようにしたり、ソーシャル ネットワーキング ストリームのどれかがアップデートされたらユーザーに通知したりするための、Android コンポーネントや UI ラベルを宣言します。

属性プレフィックス android: は、<ContactsAccountType> の属性に必須ではないことに注目してください。

属性:

{@code inviteContactActivity}
ユーザーが端末の連絡先アプリケーションで [Add Connection] を選択したときに起動する、アプリケーション内のアクティビティの完全修飾クラス名。
{@code inviteContactActionLabel}
[Add Connection] メニューで、{@code inviteContactActivity} に指定されたアクティビティ用に表示されるテキスト。 たとえば、文字列「Follow in my network」を指定できます。このラベルには文字列リソース ID を使用できます。
{@code viewContactNotifyService}
ユーザーが連絡先を表示したときに通知を受け取ることになる、独自アプリケーション内のサービスの完全修飾クラス名。 この通知は端末の連絡先アプリケーションによって送信されます。これを使用することで、アプリケーションはデータ処理の多い操作を必要になるまで延期できます。 たとえば、アプリケーションはこの通知への対応として、連絡先の高解像度写真と直近のソーシャル ストリーム アイテムを読み込んで表示できます。 この機能について詳しくは、ソーシャル ストリーム操作で説明しています。 NotifierService.java ファイルに指定された通知サービスの例は、SampleSyncAdapter サンプル アプリケーションにあります。
{@code viewGroupActivity}
グループ情報を表示できる、独自アプリケーション内のアクティビティの完全修飾クラス名。 ユーザーが端末の連絡先アプリケーションでグループラベルをタップすると、このアクティビティの UI が表示されます。
{@code viewGroupActionLabel}
ユーザーがアプリケーションでグループを表示できるようにする UI コントロールに対し、連絡先アプリケーションが表示するラベル。

たとえば、端末に Google+ アプリケーションをインストールし、Google+ を連絡先アプリケーションと同期すると、Google+ のサークルが連絡先アプリケーションの [グループ] タブにグループとして表示されます。 Google+ サークルのどれかをクリックすると、そのサークルに所属する人が「グループ」として表示されます。 表示の最上部に、Google+ アイコンが表示されます。それをクリックすると、制御が Google+ アプリに移ります。連絡先アプリケーションはこれを {@code viewGroupActivity} を使用して行い、Google+ アイコンを {@code viewGroupActionLabel} の値として使用します。

この属性には、文字列リソース ID を使用できます。

{@code viewStreamItemActivity}
ユーザーが未加工連絡先のストリーム アイテムをタップしたときに端末の連絡先アプリケーションが起動する、独自アプリケーション内のアクティビティの完全修飾クラス名。
{@code viewStreamItemPhotoActivity}
ユーザーが未加工連絡先のストリーム アイテムで写真をタップしたときに端末の連絡先アプリケーションが起動する、独自アプリケーション内のアクティビティの完全修飾クラス名。

<ContactsDataKind> 要素

<ContactsDataKind> 要素は、連絡先アプリケーションにおける独自アプリケーションのカスタムデータ行の表示を管理します。構文は次のとおりです。

<ContactsDataKind
        android:mimeType="MIMEtype"
        android:icon="icon_resources"
        android:summaryColumn="column_name"
        android:detailColumn="column_name">

含まれているファイル:

<ContactsAccountType>

説明:

この要素は、カスタムデータ行のコンテンツを未加工連絡先の詳細の一部として連絡先アプリケーションに表示させるために使用します。 <ContactsAccountType> の各 <ContactsDataKind> 子要素は、同期アダプタが {@link android.provider.ContactsContract.Data} テーブルに追加するカスタムデータ行のタイプを表します。 使用するカスタム MIME タイプごとに <ContactsDataKind> 要素を 1 つ追加します。 データを表示しないカスタムデータ行がある場合は、この要素を追加する必要はありません。

属性:

{@code android:mimeType}
{@link android.provider.ContactsContract.Data} テーブルに含まれるカスタムデータ行のどれかに定義したカスタム MIME タイプ。 たとえば、値 vnd.android.cursor.item/vnd.example.locationstatus は、連絡先の最新の場所情報を記録するデータ行のためのカスタム MIME タイプということが考えられます。
{@code android:icon}
連絡先アプリケーションがデータの隣に表示する Android ドローアブル リソース。 そのデータが独自サービスからのものであることを示すのに使用します。
{@code android:summaryColumn}
データ行から取得された 2 つの値で最初の列名。この値は、このデータ行のエントリの先頭行として表示されます。 この先頭行は、データの要約として使われることを狙ったものですが、省略可能です。 android:detailColumn もご覧ください。
{@code android:detailColumn}
データ行から取得された 2 つの値で 2 番目の列名。この値は、このデータ行のエントリの 2 行目として表示されます。 {@code android:summaryColumn} もご覧ください。

連絡先プロバイダの追加機能

ここまでのセクションで説明した主な機能の他に、連絡先プロバイダには連絡先データの作業用として次のような便利な機能が用意されています。

連絡先グループ

連絡先プロバイダでは、オプションで、関連連絡先のコレクションをグループ データでラベル付けできます。 あるユーザー アカウントに関連付けられているサーバーがグループを管理する場合、そのアカウントのアカウント タイプ用の同期アダプタが、連絡先プロバイダとサーバーとの間でグループデータを転送する必要があります。 ユーザーが新しい連絡先をサーバーに追加し、その連絡先を新しいグループに入れた場合、同期アダプタはその新しいグループを {@link android.provider.ContactsContract.Groups} テーブルに追加する必要があります。 ある未加工連絡先が属するグループ(複数の場合あり)は、{@link android.provider.ContactsContract.Data} テーブルに、{@link android.provider.ContactsContract.CommonDataKinds.GroupMembership} MIME タイプを使用して格納されます。

サーバーからの未加工連絡先データを連絡先プロバイダに追加する同期アダプタを設計している場合、グループを使用しないのであれば、連絡先プロバイダに対してデータが可視であることを通知する必要があります。 ユーザーがアカウントを端末に追加したときに実行されるコード内で、連絡先プロバイダがそのアカウントに対して追加する {@link android.provider.ContactsContract.Settings} 行をアップデートします。 この行で、{@link android.provider.ContactsContract.SettingsColumns#UNGROUPED_VISIBLE Settings.UNGROUPED_VISIBLE} 列の値を 1 に設定します。 こうすると、連絡先プロバイダは、グループを使用していない場合でも、独自の連絡先データを可視にします。

連絡先の写真

{@link android.provider.ContactsContract.Data} テーブルは、写真を MIME タイプ {@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE Photo.CONTENT_ITEM_TYPE} の行として格納します。 この行の {@link android.provider.ContactsContract.RawContactsColumns#CONTACT_ID} 列は、それが属する未加工連絡先の {@code android.provider.BaseColumns#_ID} 列にリンクされます。 クラス {@link android.provider.ContactsContract.Contacts.Photo} は、連絡先のプライマリ フォトの写真情報を格納する {@link android.provider.ContactsContract.Contacts} のサブテーブルを定義します。ここでプライマリ フォトとは、連絡先のプライマリ未加工連絡先のプライマリ フォトのことを示します。 同様に、クラス {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} は、未加工連絡先のプライマリ フォトに関する情報を含む {@link android.provider.ContactsContract.RawContacts} のサブテーブルを定義します。

{@link android.provider.ContactsContract.Contacts.Photo} と {@link android.provider.ContactsContract.RawContacts.DisplayPhoto} の参照ドキュメントには、写真情報の取得例が含まれています。 未加工連絡先のプライマリ サムネイルを取得するための便利なクラスはありませんが、{@link android.provider.ContactsContract.Data} テーブルにクエリを送信して、未加工連絡先の {@code android.provider.BaseColumns#_ID}、{@link android.provider.ContactsContract.CommonDataKinds.Photo#CONTENT_ITEM_TYPE Photo.CONTENT_ITEM_TYPE}、{@link android.provider.ContactsContract.Data#IS_PRIMARY} 列を選んでその未加工連絡先のプライマリ フォト行を探すことができます。

人のソーシャル ストリーム データにも写真が含まれていることがあります。その場合は {@code android.provider.ContactsContract.StreamItemPhotos} テーブルに格納されています。このテーブルについては、ソーシャル ストリーム フォトで詳しく説明しています