連絡先プロバイダ

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

このガイドでは、次の内容について説明します。

  • プロバイダの基本構造。
  • プロバイダからデータを取得する方法。
  • プロバイダでのデータの変更方法。
  • サーバーから 連絡先プロバイダ

このガイドは、Android コンテンツ プロバイダの基本を理解していることを前提としています。Android のコンテンツ プロバイダについて詳しくは、コンテンツ プロバイダの基本をご覧ください。

連絡先プロバイダの組織

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

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

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

ContactsContract.Contacts テーブル
未加工連絡先行の集計に基づく、さまざまな人を表す行。
ContactsContract.RawContacts テーブル
行には、ユーザー アカウントと種類に固有の、ユーザーのデータの概要が含まれます。
ContactsContract.Data テーブル
メールアドレスや電話番号など、未加工の連絡先の詳細が含まれる行。

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

未加工連絡先

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

未加工連絡先のほとんどのデータは ContactsContract.RawContacts テーブル。代わりに、ContactsContract.Data テーブルの 1 つ以上の行に格納されます。各データ行には、親 ContactsContract.RawContacts 行の RawContacts._ID 値を含む列 Data.RAW_CONTACT_ID があります。

重要な未加工連絡先の列

ContactsContract.RawContacts テーブルの重要な列は次のとおりです。 表 1 に記載されています。表の後の注もお読みください。

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

列名 使用
ACCOUNT_NAME この未加工連絡先のソースであるアカウント タイプのアカウント名。 たとえば、Google アカウントのアカウント名が、デバイス所有者の Gmail あります。次のエントリをご覧ください: ACCOUNT_TYPEするとさらに表示 情報です。 この名前の形式は、アカウント タイプによって異なります。そうではない メールアドレスである必要があります。
ACCOUNT_TYPE この未加工連絡先のソースであるアカウント タイプ。たとえば、Google アカウントのアカウント タイプは com.google です。必ずアカウントの種類を精査する ドメイン ID に置き換える必要があります。そうすることで、アカウント タイプが確実に一意になります。 連絡先データを提供するアカウント タイプには、通常、連絡先プロバイダと同期する関連同期アダプタが用意されています。
DELETED 未加工連絡先の「削除済み」フラグ。 このフラグを使用すると、連絡先プロバイダは同期が行われるまで行を内部的に維持できます。 アダプタがサーバーから行を削除し、最終的にその行を削除する リポジトリから取得します。

次の重要な注意事項は、Terraform ワークフローの ContactsContract.RawContacts テーブル:

  • 未加工連絡先の名前は、ContactsContract.RawContacts の行には格納されません。代わりに、ContactsContract.Data テーブルの ContactsContract.CommonDataKinds.StructuredName 行に格納されます。未加工連絡先は、このタイプの行を ContactsContract.Data テーブルに 1 行だけ持ちます。
  • 注意: 未加工連絡先の行で独自のアカウント データを使用する場合は、まずそれを AccountManager に登録する必要があります。登録するには、ユーザーに対し、アカウントのリストにアカウント タイプとアカウント名を追加するよう求めます。この操作を 変更すると、連絡先プロバイダは未加工の連絡先の行を自動的に削除します。

    たとえば、アプリでドメイン com.example.dataservice のウェブベース サービスの連絡先データを維持し、サービスのユーザー アカウントが becky.sharp@dataservice.example.com である場合、アプリが未加工の連絡先行を追加するには、ユーザーがまずアカウントの「タイプ」(com.example.dataservice)とアカウントの「名前」(becky.smart@dataservice.example.com)を追加する必要があります。この要件は、ドキュメントでユーザーに説明することも、プロンプトで タイプと名前、またはその両方を追加できます。アカウントのタイプと名前については、次のセクションで詳しく説明します。

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

未加工連絡先の仕組みを理解するには、ユーザーを「Emily Dickinson」とします。次の権限がある デバイスに定義されている 3 つのユーザー アカウント:

  • emily.dickinson@gmail.com
  • emilyd@gmail.com
  • Twitter アカウント「belle_of_amherst」

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

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

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

  1. 「Thomas Higginson」の未加工連絡先emily.dickinson@gmail.com に関連付けられている。 ユーザー アカウントのタイプは Google です。
  2. 「Thomas Higginson」の、emilyd@gmail.com に関連付けられた新たな未加工連絡先。 ユーザー アカウントの種類も Google です。別の未加工連絡先も入力されています。 ただし、以前の名前と同じです。 アクセスできません。
  3. 「Thomas Higginson」の、「belle_of_amherst」に関連付けられた未加工連絡先。ユーザー アカウントの種類が Twitter。

データ

前述のように、未加工連絡先のデータは 未加工連絡先にリンクされる ContactsContract.Data_ID 値。これにより、1 つの未加工連絡先に同じ内容の複数のインスタンスを データの種類を選択できます。たとえば、emilyd@gmail.com の「Thomas Higginson」(Google アカウント emilyd@gmail.com に関連付けられた Thomas Higginson の未加工連絡先行)に自宅のメールアドレスが thigg@gmail.com で、職場のメールアドレスが thomas.higginson@gmail.com の場合、連絡先プロバイダは 2 つのメールアドレス行を保存し、両方を未加工連絡先にリンクします。

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

説明的な名前の列

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

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

汎用名の列

DATA1DATA15 という 15 個の汎用列が一般公開されており、SYNC1SYNC4 という 4 個の汎用列は同期アダプタでのみ使用する必要があります。汎用名列の定数は、行に指定されているデータのタイプによらず、常に機能します。

DATA1 列はインデックスに登録されています。連絡先プロバイダは常に、この列を プロバイダが期待するデータが、最も頻繁にクエリのターゲットとなる場合。たとえば、メールの行では、この列には実際のメールアドレスが格納されます。

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

タイプ固有の列名

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

たとえば、ContactsContract.CommonDataKinds.Email クラスは、 ContactsContract.Data 行の型固有の列名定数 が MIME タイプです。 Email.CONTENT_ITEM_TYPE。クラスには、この定数が メールアドレス用の ADDRESS 選択します。ADDRESS の実際の値は「data1」で、これは列の汎用名と同じです。

注意: 独自のカスタムデータを テーブルのいずれかを含む行を使用して、ContactsContract.Data テーブルを MIME タイプを拒否できます。無効にした場合、データが失われたり、プロバイダが 防ぐことができます。たとえば、メールアドレスではなくユーザー名が格納されている MIME タイプ Email.CONTENT_ITEM_TYPE の行は、列 DATA1 には追加しないでください。一方、行に独自のカスタム MIME タイプを使用している場合は、独自のタイプ固有名を定義して列を自由に使用してかまいません。

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

タイプ固有の列名を汎用列名に対応させる方法

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

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

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

表 2. 型固有の列名クラス

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

連絡先

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

注: 連絡先プロバイダに連絡先を追加しようとしたときに insert() 様、 UnsupportedOperationException 例外。列を更新しようとすると、 「読み取り専用」と表示されている場合更新は無視されます。

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

連絡先プロバイダは、連絡先の行を未加工連絡先とリンクするのに、Contacts テーブルの連絡先行の _ID 列を使用します。未加工連絡先テーブルの CONTACT_IDContactsContract.RawContacts に含まれる _ID 個の値 未加工連絡先の各行に関連付けられた連絡先行を 表します

ContactsContract.Contacts テーブルには、連絡先の行への「永久」リンクである LOOKUP_KEY 列もあります。連絡先プロバイダが連絡先を保持するため 連絡先行の _ID 値が変更されることがあります。 集計または同期に対してレスポンスが 返されるためですこの場合でも、コンテンツ URI は CONTENT_LOOKUP_URIとの組み合わせ 連絡先の LOOKUP_KEY は引き続き 連絡先の行にカーソルを合わせるだけで、 LOOKUP_KEY 「お気に入り」へのリンクを維持するには、連絡先などがあります。この列には独自の書式があります _ID 列の形式とは無関係です。

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

連絡先プロバイダの主なテーブル

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

注意: アプリを Google Play ストアに公開した場合、または アプリが Android 10(API レベル 29)以降を搭載しているデバイス上にある場合、 一部の連絡先データのフィールドとメソッドが廃止されました。

上記の条件下で、システムは定期的に値をクリアします。 次のデータ フィールドに書き込まれます。

上記のデータ フィールドの設定に使用される API も廃止されています。

また、次のフィールドでは、よく使う連絡先が返されなくなりました。これらのフィールドの一部は、連絡先が特定のデータの種類に属している場合にのみ、連絡先のランキングに影響します。

アプリがこれらのフィールドや API にアクセスしたり更新したりする場合は、代わりに あります。たとえば、Cloud Functions を使用して特定のユースケースを 限定公開 コンテンツ プロバイダ、またはアプリまたはバックエンド内に保存されているその他のデータ 支援します

アプリの機能がこの変更の影響を受けていないことを確認するには、これらのデータ フィールドを手動で消去します。そのためには、次の ADB を実行します。 コマンドを使用するには、Android 4.1(API レベル 16)以降を搭載しているデバイスを使用する必要があります。

adb shell content delete \
--uri content://com.android.contacts/contacts/delete_usage

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

ユーザーは連絡先データをデバイスに直接入力しますが、データは連絡先にも取り込まれます 自動化する同期アダプターを介したウェブサービスからプロバイダー デバイスとサービス間のデータ転送。同期アダプターがバックグラウンドで実行される システムの制御下にあり、ContentResolver メソッドを呼び出します。 データを管理するためのものです。

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

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

アカウント タイプは一意である必要はありません。ユーザーは複数の Google コンタクト アカウントを設定できます 連絡先プロバイダにユーザーデータをダウンロードする。これは、ユーザーにいずれかのパスワードが それぞれ個人用の連絡先と仕事用のアカウント名がありますアカウント名は通常一意です。この 2 つを組み合わせて、連絡先プロバイダと外部サービスとの間のある決まったデータフローを識別します。

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

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

人に関するデータの流れ

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

必要な権限

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

1 つ以上のテーブルに対する読み取りアクセス
READ_CONTACTS、指定 AndroidManifest.xml は、 <uses-permission> 要素の形式 <uses-permission android:name="android.permission.READ_CONTACTS">
1 つ以上のテーブルへの書き込みアクセス権
WRITE_CONTACTS、指定 AndroidManifest.xml は、 <uses-permission> 要素の形式 <uses-permission android:name="android.permission.WRITE_CONTACTS">

これらの権限は、ユーザー プロファイル データには適用されません。ユーザー プロフィールとそのプロフィール 必要な権限については、次のセクションで説明します。 ユーザー プロファイル

ユーザーの連絡先データは個人情報であり機密情報です。ユーザーが懸念していること 自分のプライバシーや連絡先に関するデータをアプリケーションで収集することは避けたいと考えています。 連絡先データにアクセスするための権限が必要な理由が明らかでないと、ユーザーがアプリを低く評価したり、インストールを拒否したりすることがあります。

ユーザー プロフィール

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

ユーザー プロファイルにアクセスするには、特別なパーミッションが必要です。このコースでは、 READ_CONTACTS、 読み取りと書き込み、アクセスには WRITE_CONTACTS 権限が必要です ユーザー プロファイルに追加するには、android.Manifest.permission#READ_PROFILE が必要です。また、 読み取り / 書き込みアクセスのための android.Manifest.permission#WRITE_PROFILE 権限 できます。

ユーザーのプロフィールは機密情報として扱う必要があります。パーミッション android.Manifest.permission#READ_PROFILE を使用すると、デバイス ユーザーの個人識別データにアクセスできます。必ずその理由をお客様に伝えます。 アプリケーションの説明にユーザー プロファイルのアクセス権限が必要です。

ユーザーのプロファイルが格納された連絡先の行を取得するには、ContentResolver.query() を呼び出します。コンテンツ URI を次のように設定します: CONTENT_URIで、何も指定しない 選択基準を指定しますまた、このコンテンツ URI を未加工の URL を取得するためのベース URI として使用することもできます。 プロファイル用の連絡先またはデータを指定します。たとえば、次のスニペットは指定されたプロファイルのデータを取得します。

Kotlin

// Sets the columns to retrieve for the user profile
projection = arrayOf(
        ContactsContract.Profile._ID,
        ContactsContract.Profile.DISPLAY_NAME_PRIMARY,
        ContactsContract.Profile.LOOKUP_KEY,
        ContactsContract.Profile.PHOTO_THUMBNAIL_URI
)

// Retrieves the profile from the Contacts Provider
profileCursor = contentResolver.query(
        ContactsContract.Profile.CONTENT_URI,
        projection,
        null,
        null,
        null
)

Java

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

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

注: 連絡先の行を複数取得する場合、そのなかの 1 つがユーザー プロファイルかどうかを確認するには、行の IS_USER_PROFILE 列をテストします。この列 「1」に設定されます連絡先がユーザー プロフィールである場合。

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

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

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

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

未加工連絡先やデータのテーブルを変更する同期アダプタは、使用するコンテンツ URI の末尾に文字列 CALLER_IS_SYNCADAPTER を必ず追加してください。これにより、プロバイダが行をダーティとマークするのを防ぐことができます。それ以外の場合、同期アダプターの変更はローカルの変更のように見え、 変更のソースがサーバーであっても、サーバーに送信されます。

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

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

  • 一意: アカウントの各未加工連絡先には独自のソース ID が必要です。もし 強制しないと、連絡先アプリケーションで問題が発生します。 同じアカウント タイプに対する 2 つの未加工連絡先が、同じソース ID を持つことがありえます。たとえば、アカウント emily.dickinson@gmail.com の未加工連絡先「Thomas Higginson」は、アカウント emilyd@gmail.com の未加工連絡先「Thomas Higginson」と同じソース ID を持つことができます。
  • 安定: ソース ID は、未加工連絡先のオンライン サービス データにおいて変わらない部分です。たとえば、ユーザーが アプリの設定と再同期。復元された未加工連絡先は、 渡します。これを強制しないと、ショートカットが機能しなくなります。
ContactsContract.Groups GROUP_VISIBLE 「0」 - このグループに属する連絡先が Android アプリケーションの UI に表示されなくなります。 この列は、ユーザーが できます。
「1」- このグループの連絡先をアプリの UI に表示できます。
ContactsContract.Settings UNGROUPED_VISIBLE 「0」- このアカウントとアカウント タイプについて、グループに属さない連絡先は Android アプリケーションの UI に表示されなくなります。 デフォルトでは、未加工連絡先がグループに含まれていない場合、その連絡先は非表示になります (未加工連絡先のグループ メンバーシップは、1 つ以上の ContactsContract.CommonDataKinds.GroupMembership 行 (ContactsContract.Data テーブル)をご覧ください)。 あるアカウントとアカウント タイプについて、ContactsContract.Settings テーブルの行でこのフラグを設定すると、グループのない連絡先を強制的に表示できます。このフラグの用途の 1 つとして、グループを使用しないサーバーからの連絡先を表示することが挙げられます。
「1」- このアカウントとアカウント タイプについて、グループに属さない連絡先が Android アプリケーションの UI に表示されます。
ContactsContract.SyncState (すべて) このテーブルを使用して、同期アダプタのメタデータを格納します。 このテーブルを使用すると、同期状態やその他の同期関連データを永続的に保存し、 クリックします。

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

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

  • エンティティ クエリ。
  • バッチ変更。
  • インテントを使用した取得と変更。
  • データの整合性。

同期アダプターによる変更については、この後のセクションで詳しく説明します。 連絡先プロバイダの同期アダプター

エンティティのクエリ

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

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

エンティティにより、クエリが簡素化されます。エンティティを使用すると、特定の組織のすべての連絡先データを取得できます。 未加工連絡先や未加工連絡先を一度にクエリできます。 その ID で子テーブルをクエリする必要があります。また連絡先プロバイダは 1 回のトランザクションで 1 つのエンティティに対してクエリを実行する。これにより、取得されたデータが確実に 内部的に一貫性があります。

注: 通常、1 つのエンティティに親の列のすべてが含まれるわけではなく、 子テーブルですそのエンティティの列名定数のリストにない列名に対して作業しようとすると、Exception が発生します。

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

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

Kotlin

...
    /*
     * 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).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            ContactsContract.Contacts.Entity.CONTENT_DIRECTORY
    )

    // Initializes the loader identified by LOADER_ID.
    loaderManager.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
    cursorAdapter = SimpleCursorAdapter(
            this,                       // the context of the activity
            R.layout.detail_list_item,  // the view item containing the detail widgets
            mCursor,                    // the backing cursor
            fromColumns,               // the columns in the cursor that provide the data
            toViews,                   // the views in the view item that display the data
            0)                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.adapter = cursorAdapter
...
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> {
    /*
     * 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.
     */
    val projection: Array<String> = arrayOf(
            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.
     */
    val 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 CursorLoader(
            applicationContext, // The activity's context
            contactUri,        // 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.
    )
}

Java

...
    /*
     * 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).
     */
    contactUri = Uri.withAppendedPath(
            contactUri,
            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
    cursorAdapter = new SimpleCursorAdapter(
            this,                        // the context of the activity
            R.layout.detail_list_item,   // the view item containing the detail widgets
            mCursor,                     // the backing cursor
            fromColumns,                // the columns in the cursor that provide the data
            toViews,                    // the views in the view item that display the data
            0);                          // flags

    // Sets the ListView's backing adapter.
    rawContactList.setAdapter(cursorAdapter);
...
@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
            contactUri,              // 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.
}

読み込みが完了すると、LoaderManageronLoadFinished() へのコールバックを呼び出します。このメソッドへの入力引数の 1 つは、クエリの結果を含む Cursor です。独自のアプリでは、 この Cursor のデータを表示して、表示したり操作したりできます。

バッチ変更

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

注: 1 つの未加工連絡先を変更するには、インテントを アプリで変更を処理するのではなく、デバイスの連絡先アプリを使用します。 これについては、この後のセクションで詳しく説明します。 インテントを使用した取得と変更

明け方

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

明け渡し点により、applyBatch() の呼び出し 1 回につき複数のトランザクションが発生します。そのため、明け渡し点は 1 組の関連する行への最後の操作に設定してください。たとえば、追加のアクションを追加するセット内の最後のオペレーションに明け渡し時点を 未加工連絡先の行とそれに関連付けられたデータ行、または関連する行のセットに対する最後のオペレーション 1 つの連絡先に割り当てることができます。

明け渡し点は、アトミック オペレーションの単位でもあります。2 つの明け渡し点間のすべてのアクセスは、1 つのまとまりとして成功または失敗します。明け渡し点を設定しない場合、最小のアトミック操作は操作のバッチ全体になります。収益ポイントを使用すると システム パフォーマンスの低下を防ぎながら、運用効率を低下させることなく、 オペレーションはアトミックです。

変更の後方参照

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

withValueBackReference() メソッドには次の 2 つの引数があります。

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

結果の配列は、最初に applyBatch(), サイズが ArrayList と同じサイズで、 ContentProviderOperation オブジェクトを指定します。ただし、結果配列に含まれるすべての要素は null に設定され、そのためまだ適用されていない操作から結果を後方参照しようとすると withValueBackReference()Exception をスローします。

次のスニペットは、新しい未加工の連絡先とデータをバッチで挿入する方法を示しています。。 明け渡し点を確立し、後方参照を使用するコードを組み込む。

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

Kotlin

// Creates a contact entry from the current UI values, using the currently-selected account.
private fun createContactEntry() {
    /*
     * Gets values from the UI
     */
    val name = contactNameEditText.text.toString()
    val phone = contactPhoneEditText.text.toString()
    val email = contactEmailEditText.text.toString()

    val phoneType: String = contactPhoneTypes[mContactPhoneTypeSpinner.selectedItemPosition]

    val emailType: String = contactEmailTypes[mContactEmailTypeSpinner.selectedItemPosition]

Java

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

    int phoneType = contactPhoneTypes.get(
            contactPhoneTypeSpinner.getSelectedItemPosition());

    int emailType = contactEmailTypes.get(
            contactEmailTypeSpinner.getSelectedItemPosition());

次のスニペットは、未加工連絡先の行を ContactsContract.RawContacts テーブル:

Kotlin

    /*
     * 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.
    val ops = arrayListOf<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.
     */
    var op: ContentProviderOperation.Builder =
            ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.name)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.type)

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

Java

    /*
     * 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, selectedAccount.getType())
            .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.getName());

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

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

各オペレーション ビルダー オブジェクトは、withValueBackReference() を使用して RAW_CONTACT_ID を取得します。この参照が最初の操作からの ContentProviderResult オブジェクトを後方参照しており、それが未加工連絡先の行を追加し、新しい _ID 値を返します。その結果、各データ行は、 RAW_CONTACT_ID 新しい ContactsContract.RawContacts 行に追加してください。

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

Kotlin

    // 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())

Java

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

最後のスニペットは、新しい未加工連絡先とデータ行を挿入する applyBatch() を呼び出しています。

Kotlin

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG, "Selected account: ${mSelectedAccount.name} (${mSelectedAccount.type})")
    Log.d(TAG, "Creating contact: $name")

    /*
     * Applies the array of ContentProviderOperation objects in batch. The results are
     * discarded.
     */
    try {
        contentResolver.applyBatch(ContactsContract.AUTHORITY, ops)
    } catch (e: Exception) {
        // Display a warning
        val txt: String = getString(R.string.contactCreationFailure)
        Toast.makeText(applicationContext, txt, Toast.LENGTH_SHORT).show()

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

Java

    // Ask the Contacts Provider to create a new contact
    Log.d(TAG,"Selected account: " + selectedAccount.getName() + " (" +
            selectedAccount.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 人でデータリポジトリへの同時アクセスがまれなモバイル端末に便利です。ロックが使用されないため、ロックの設定や他のトランザクションによるロックの解放待ちで時間を無駄にしません。

1 つの ContactsContract.RawContacts 行を更新するときに楽観的並行性制御を使用する手順は次のとおりです。

  1. 未加工連絡先の VERSION を取得する 取得する他のデータと一緒に 表示できます
  2. 制約を適用するのに適した ContentProviderOperation.Builder オブジェクトを、メソッド newAssertQuery(Uri) を使用して作成します。コンテンツ URI については、 RawContacts.CONTENT_URI を使用する 未加工連絡先の _ID が追加されます。
  3. ContentProviderOperation.Builder オブジェクトに対して withValue() を呼び出して、VERSION 列と取得したばかりのバージョン番号を比較します。
  4. 同じ ContentProviderOperation.Builder に対して、次の関数を呼び出します。 withExpectedCount(): このアサーションによってテストされる行が 1 つだけであることを確認します。
  5. build() を呼び出して ContentProviderOperation オブジェクトを作成し、このオブジェクトを applyBatch() に渡す ArrayList の最初のオブジェクトとして追加します。
  6. バッチ トランザクションを適用します。

未加工連絡先の行が、行の読み取りから 変更しようとすると、「assert」ContentProviderOperation オペレーションのバッチ全体がバックアップされます。その後、再試行を選択できます その他のアクションを実行できます

次のスニペットは、CursorLoader を使用して未加工連絡先を 1 つクエリした後に、ContentProviderOperation を「アサート」する方法を示しています。

Kotlin

/*
 * The application uses CursorLoader to query the raw contacts table. The system calls this method
 * when the load is finished.
 */
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
    // Gets the raw contact's _ID and VERSION values
    rawContactID = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID))
    mVersion = cursor.getInt(cursor.getColumnIndex(SyncColumns.VERSION))
}

...

// Sets up a Uri for the assert operation
val rawContactUri: Uri = ContentUris.withAppendedId(
        ContactsContract.RawContacts.CONTENT_URI,
        rawContactID
)

// Creates a builder for the assert operation
val assertOp: ContentProviderOperation.Builder =
        ContentProviderOperation.newAssertQuery(rawContactUri).apply {
            // Adds the assertions to the assert operation: checks the version
            withValue(SyncColumns.VERSION, mVersion)

            // and count of rows tested
            withExpectedCount(1)
        }

// Creates an ArrayList to hold the ContentProviderOperation objects
val ops = arrayListOf<ContentProviderOperation>()

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 {
    val results: Array<ContentProviderResult> = contentResolver.applyBatch(AUTHORITY, ops)
} catch (e: OperationApplicationException) {
    // Actions you want to take if the assert operation fails go here
}

Java

/*
 * 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
    rawContactID = 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, rawContactID);

// Creates a builder for the assert operation
ContentProviderOperation.Builder assertOp = ContentProviderOperation.newAssertQuery(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<ContentProviderOperation>;

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 で 連絡先関連の作業を行えますこの種類のアクセス権では、ユーザーは以下の操作を行えます。

  • 連絡先をリストから選び、アプリケーションに返してさらに作業する。
  • 既存の連絡先データを編集する。
  • 任意のアカウントに新しい未加工連絡先を挿入する。
  • 連絡先または連絡先データを削除する。

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

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

プロバイダにアクセスするためにインテントを送信する一般的なプロセスについては、 <ph type="x-smartling-placeholder"></ph> コンテンツ プロバイダの基本ガイドの「インテントを介したデータアクセス」セクションを参照してください。アクション MIME タイプ、利用可能なタスクに使用するデータ値を表 4 に示します。 エクストラ値を使用できます。 putExtra() のリストは、 ContactsContract.Intents.Insert のリファレンス ドキュメント:

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

タスク アクション データ MIME タイプ
リストから連絡先を選択する ACTION_PICK 次のいずれか: 不使用 指定したコンテンツ URI のタイプに応じて、未加工連絡先のリストか、未加工連絡先から取得されたデータのリストを表示します。

発信 startActivityForResult(), 選択した行のコンテンツ URI が返されます。URI の形式は、 行の LOOKUP_ID が追加されたテーブルのコンテンツ URI。 端末の連絡先アプリは、このアクティビティの実行中を通して、読み取りと書き込みのパーミッションをこのコンテンツ URI にデリゲートします。詳しくは、コンテンツ プロバイダの基本ガイドをご覧ください。

新しい未加工連絡先を挿入する Insert.ACTION なし RawContacts.CONTENT_TYPE: 未加工連絡先のセットに対する MIME タイプ。 デバイスの連絡先アプリの [連絡先の追加] 画面を表示します。「 インテントに追加したエクストラ値が表示されます。次の形式で送信する場合: startActivityForResult(), 新しく追加された未加工連絡先のコンテンツ URI が、アクティビティの onActivityResult() コールバック メソッドを、Intent 引数、 「データ」表示されます。値を取得するには、getData() を呼び出します。
連絡先を編集する ACTION_EDIT CONTENT_LOOKUP_URI: 表示されます。エディタ アクティビティを使用すると、ユーザーがその連絡先に関連付けられている任意のデータを編集できます。 Contacts.CONTENT_ITEM_TYPE: 単一の連絡先。 連絡先アプリケーションに連絡先の編集画面を表示します。追加するエクストラ値 表示されます。ユーザーが [完了] をクリックして編集内容を保存すると、アクティビティがフォアグラウンドに戻ります。
データの追加もできる選択ツールを表示します。 ACTION_INSERT_OR_EDIT なし CONTENT_ITEM_TYPE このインテントは、連絡帳アプリの選択画面を常に表示します。ユーザーは次のいずれかの方法で 編集する連絡先を選択するか、新しい連絡先を追加します。ユーザーの選択に応じて編集画面か追加画面が開き、インテントに含めて渡したエクストラ データが表示されます。アプリにメールアドレスや電話番号などの連絡先データを表示する場合は、 このインテントによってユーザーが既存の連絡先にデータを追加できるようにします。 連絡先、

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

デバイスの連絡帳アプリでは、 使用します。代わりに、未加工連絡先を削除するには ContentResolver.delete() または ContentProviderOperation.newDelete() を使用します。

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

Kotlin

// Gets values from the UI
val name = contactNameEditText.text.toString()
val phone = contactPhoneEditText.text.toString()
val email = contactEmailEditText.text.toString()

val company = companyName.text.toString()
val jobtitle = jobTitle.text.toString()

/*
 * 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
val contactData = arrayListOf<ContentValues>()

/*
 * Defines the raw contact row
 */

// Sets up the row as a ContentValues object
val rawContactRow = ContentValues().apply {
    // Adds the account type and name to the row
    put(ContactsContract.RawContacts.ACCOUNT_TYPE, selectedAccount.type)
    put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.name)
}

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

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

// Sets up the row as a ContentValues object
val phoneRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE,ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)

    // Adds the phone number and its type to the row
    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
val emailRow = ContentValues().apply {
    // Specifies the MIME type for this data row (all data rows must be marked by their type)
    put(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE)

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

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

// Creates a new intent for sending to the device's contacts application
val insertIntent = Intent(ContactsContract.Intents.Insert.ACTION).apply {
    // Sets the MIME type to the one expected by the insertion activity
    type = ContactsContract.RawContacts.CONTENT_TYPE

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

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

    /*
    * 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
    */
    putParcelableArrayListExtra(ContactsContract.Intents.Insert.DATA, contactData)
}

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

Java

// Gets values from the UI
String name = contactNameEditText.getText().toString();
String phone = contactPhoneEditText.getText().toString();
String email = contactEmailEditText.getText().toString();

String company = companyName.getText().toString();
String jobtitle = jobTitle.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, selectedAccount.getType());
rawContactRow.put(ContactsContract.RawContacts.ACCOUNT_NAME, selectedAccount.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);

データの整合性

連絡先リポジトリには、ユーザーが期待する重要かつセンシティブ データが含まれているため 連絡先プロバイダには、データの整合性について明確に定義されたルールがあります。です。 連絡先データを変更する際は、これらのルールを遵守するユーザーの責任があります。重要なルールは次のとおりです。

追加する ContactsContract.RawContacts 行ごとに、必ず ContactsContract.CommonDataKinds.StructuredName 行を追加してください。
ContactsContract.Data テーブルに ContactsContract.CommonDataKinds.StructuredName 行のない ContactsContract.RawContacts 行は、集約中に問題を引き起こすことがあります。
新しい ContactsContract.Data 行は、必ず親の ContactsContract.RawContacts 行にリンクしてください。
商品にリンクされていない ContactsContract.Data 行が ContactsContract.RawContacts はデバイスの 同期アダプターで問題が発生することがあります。
自分が所有している未加工連絡先のデータのみを変更する。
通常、連絡先プロバイダは複数の異なるアカウント タイプやオンライン サービスから取得したデータを管理しています。アプリケーションが特定のリソースのみを データを削除したり削除したりできるほか、データが挿入される 管理できるアカウントの種類と名前です。
常に ContactsContract とその変数で定義されている定数を使用する 認証、コンテンツ URI、URI パス、列名、MIME タイプ、 TYPE 値。
これらの定数を使用することがエラーの予防に役立ちます。また、使われなくなった定数があれば、コンパイラーから警告として通知されます。

カスタムデータ行

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

カスタムデータを表示するには、<ContactsAccountType> 要素とその <ContactsDataKind> 子要素が 1 つ以上含まれている contacts.xml ファイルを指定します。詳しくは、 セクション <ContactsDataKind> element

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

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

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

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

  • ネットワークの可用性を確認しています。
  • ユーザー設定に基づく同期のスケジューリングと実行。
  • 停止された同期の再開。

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

アダプターのクラスとファイルを同期する

同期アダプターは、Terraform のサブクラスとして AbstractThreadedSyncAdapter をインストールし、Android アプリの一部としてインストールします。 説明します。システムがアプリの要素から同期アダプターについて学習する マニフェストで指定される特別な XML ファイルを使用します。XML ファイルでは、 オンライン サービスのアカウントの種類とコンテンツ プロバイダの権限の 2 つがあり、これらを 一意に識別する必要があります。同期アダプタは、ユーザーが同期アダプタのアカウント タイプに対してアカウントを追加し、同期アダプタの同期対象である連絡先プロバイダで同期を有効にすることで、アクティブになります。この時点で、システムはアダプタの管理を開始し、連絡先プロバイダとサーバーとの同期が必要になるとそれを呼び出します。

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

ほとんどのサービスでは、ユーザーがアクセスする前に本人確認が求められるため Android システムは認証フレームワークを提供します。このフレームワークは、 使用できます。この認証フレームワークでは、AbstractAccountAuthenticator のサブクラスであるプラグイン認証システムを使用します。認証システムは、次の手順でユーザーの身元を検証します。

  1. ユーザー名、パスワード、または同様の情報(ユーザーの 認証情報を参照)。
  2. サービスに認証情報を送信する
  3. サービスの返答を検証します。

サービスが認証情報を受け入れた場合、認証システムはその認証情報を後での使用のために格納します。プラグイン認証システムのフレームワークにより、 AccountManager は、任意の authtoken と認証システムへのアクセスを提供できます。 OAuth2 認証トークンなど、どの API が

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

同期アダプターの実装

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

システムからのリクエストに応答する Service コンポーネント 同期アダプターにバインドします
システムが同期を実行しようとすると、サービスの onBind() メソッドを呼び出して、同期アダプタの IBinder を取得します。これによりシステムは アダプタのメソッドへのプロセス間呼び出しを一括処理します。
AbstractThreadedSyncAdapter の具象サブクラスとして実装された、実際の同期アダプタ。
このクラスが、サーバーからのデータのダウンロード、デバイスからのデータのアップロード、競合の解消といった作業を実施します。アダプタの主な処理は、メソッド onPerformSync() で行われます。このクラスはシングルトンとしてインスタンス化する必要があります。
Application のサブクラス。
このクラスは同期アダプタ シングルトンのファクトリとして機能します。こちらの onCreate() メソッドを使用して同期アダプターをインスタンス化する 静的な「ゲッター」を提供します。メソッドを呼び出し、シングルトンを 同期アダプターの onBind() メソッド あります。
省略可: 次に応答する Service コンポーネント ユーザー認証リクエスト。
AccountManager がこのサービスを開始して認証を開始します プロセスですサービスの onCreate() メソッドは、認証システム オブジェクトをインスタンス化します。システムがユーザーの認証を求めて、 同期アダプターを呼び出すと、サービスの onBind() メソッドを使用して、 認証システムの IBinder。これにより、システムは認証システムのメソッドに対するプロセス間呼び出しを実行できます。
省略可: 認証のリクエストを処理する AbstractAccountAuthenticator の具象サブクラス。
このクラスは、AccountManager が呼び出すメソッドを提供します。 サーバーでユーザーの認証情報を認証します。kubectl の 認証プロセスは、使用するサーバー技術によって大きく異なります。すべきこと 認証について詳しくは、お使いのサーバー ソフトウェアのドキュメントをご覧ください。
システムに対する同期アダプターと認証システムを定義する XML ファイル。
前述の同期アダプターと認証システムのサービス コンポーネントは、 定義 <service> 要素を宣言します。これらの要素 <meta-data> システムに特定のデータを提供する子要素:
  • <meta-data> 要素が、同期アダプター サービスの XML ファイル res/xml/syncadapter.xml。また、このファイルには、 連絡先プロバイダと同期されるウェブサービスの URI ウェブサービスのアカウントタイプを指定します
  • 省略可: 認証システムの <meta-data> 要素は、XML ファイル res/xml/authenticator.xml を指します。このファイルが、この認証システムがサポートするアカウント タイプと、認証プロセスの最中に表示される UI リソースを指定します。この要素に指定されるアカウント タイプは、同期アダプタ用に指定されるアカウント タイプと同じであることが必要です。

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

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

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

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

ストリームのデータを次の列に保存します。

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

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

android.provider.ContactsContract.StreamItems テーブルには、同期アダプタ専用に列 android.provider.ContactsContract.StreamItemsColumns#SYNC1 ~ android.provider.ContactsContract.StreamItemsColumns#SYNC4 も含まれます。

ソーシャル ストリームの写真

android.provider.ContactsContract.StreamItemPhotos テーブルには、関連付けられている写真が ストリーム アイテムと関連付けます。このテーブルの android.provider.ContactsContract.StreamItemPhotosColumns#STREAM_ITEM_ID 列は、android.provider.ContactsContract.StreamItems テーブルの _ID 列の値にリンクされます。写真の参照は 列:

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

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

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

  • これらのテーブルには、追加のアクセス パーミッションが必要です。読み取るには、アプリケーションで android.Manifest.permission#READ_SOCIAL_STREAM 権限が必要です。宛先 変更する場合は、アプリケーションにそのアプリケーションに必要な権限を android.Manifest.permission#WRITE_SOCIAL_STREAM です。
  • android.provider.ContactsContract.StreamItems テーブルについては、各未加工連絡先を格納できる行数に上限があります。この上限に達すると 連絡先プロバイダによって自動的に削除され、新しいストリーム アイテムの行のスペースが確保されます 最も古いものを含む android.provider.ContactsContract.StreamItemsColumns#TIMESTAMP を実行します。上限を取得するには、コンテンツ URI android.provider.ContactsContract.StreamItems#CONTENT_LIMIT_URI にクエリを発行します。退出できます null に設定されたコンテンツ URI 以外のすべての引数。このクエリは、列が android.provider.ContactsContract.StreamItems#MAX_ITEMS だけの 1 行を含む Cursor を返します。

クラス android.provider.ContactsContract.StreamItems.StreamItemPhotos は、 写真を含む android.provider.ContactsContract.StreamItemPhotos のサブテーブル 1 行につき 1 行までです

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

連絡先プロバイダによって管理されるソーシャル ストリーム データと、 ソーシャル ネットワーク システムを接続する強力な手段となります。 既存の連絡先と共有できます次の機能を使用できます。

  • 同期によってソーシャル ネットワーク サービスを連絡先プロバイダに同期する アダプタを使用すると、ユーザーの連絡先の最近のアクティビティを取得して、 android.provider.ContactsContract.StreamItems と android.provider.ContactsContract.StreamItemPhotos テーブルを後で使用するために使用できます。
  • 定期的な同期の他に、同期アダプターをトリガーして 表示する連絡先を選択したときに追加データを表示できます。これにより同期アダプターが を使用して、連絡先の高解像度写真と最新のストリーム アイテムを取得できます。
  • 端末の連絡先アプリケーションと連絡先プロバイダに関する通知を登録すると、連絡先が表示された場合にインテントを受信し、その時点で連絡先のステータスをサービスからアップデートできます。このアプローチは、同期アダプタを使用してフル同期を実施するより、処理が速く、帯域幅が節約されます。
  • ユーザーは連絡先を見ながら、ソーシャル ネットワーク サービスの連絡先を追加できます 。この機能は [連絡先を招待] で有効にできます。機能 既存の連絡先をアカウントに追加するアクティビティの組み合わせで有効にできます。 XML ファイルが含まれます。このファイルには、デバイスの連絡先アプリと 連絡先プロバイダにアプリの詳細を送信します。

連絡先プロバイダとのストリーム アイテムの定期的な同期は、 同期できます。同期について詳しくは、このモジュールの 連絡先プロバイダの同期アダプター通知の登録と連絡先の招待については、次の 2 つのセクションで説明します。

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

同期アダプターを登録して、連絡先が閲覧されている連絡先を表示したときに通知を受け取るには、 同期アダプターによって管理されているもの:

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

ユーザーがストリーム アイテムか写真、またはその両方をクリックしたときに呼び出されるアクティビティを登録するには:

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

<ContactsAccountType> 要素の詳細については、 セクション <ContactsAccountType>要素をご覧ください。

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

ソーシャル ネットワーキング サービスとのやり取り

ユーザーは、連絡先をソーシャル ネットワーキング サービスに招待するのに、端末の連絡先アプリケーションを離れる必要はありません。代わりに、デバイスの連絡帳アプリから アクティビティのいずれかに 関連付けられます手順は以下のとおりです。

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

注: ContactsSourceContactsAccountType の非推奨のタグ名です。

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>

属性:

inviteContactActivity
アプリケーション内で実行するアクティビティの完全修飾クラス名 ユーザーがデバイスの [接続を追加] を選択すると有効になります。 。
inviteContactActionLabel
[接続を追加] メニューで、inviteContactActivity に指定されたアクティビティ用に表示されるテキスト。 たとえば、「フォロー中ネットワーク」という文字列を使用できます。このラベルには文字列リソース ID を使用できます。
viewContactNotifyService
アプリケーション内のサービスの完全修飾クラス名。 ユーザーが連絡先を表示したときに通知を受け取ります。この通知は、デバイスの 連絡先アプリケーションアプリケーションで大量のデータを使用するオペレーションを延期できる 使用し続けることができます。たとえば、アプリケーションはこの通知への対応として、連絡先の高解像度写真と直近のソーシャル ストリーム アイテムを読み込んで表示できます。この機能について詳しくは、このモジュールの ソーシャル ストリーム インタラクション
viewGroupActivity
アプリ内で表示できるアクティビティの完全修飾クラス名 グループ情報。ユーザーがデバイスの連絡先のグループラベルをクリックしたとき このアクティビティの UI が表示されます。
viewGroupActionLabel
ユーザーがアプリケーションでグループを表示できるようにする UI コントロールに対し、連絡先アプリケーションが表示するラベル。

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

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

<ContactsDataKind> 要素

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

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

含まれるもの:

<ContactsAccountType>

説明:

この要素を使用すると、連絡先アプリケーションにカスタムデータ行の内容を 未加工連絡先の詳細の一部の情報が含められます各 <ContactsDataKind> 子要素 <ContactsAccountType> は、同期するカスタムデータ行の種類を表します ContactsContract.Data テーブルに追加されます。追加 使用する各カスタム MIME タイプの <ContactsDataKind> 要素。リソースがありません データを表示したくないカスタムデータ行がある場合は、要素を追加します。

属性:

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

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

前のセクションで説明した主な機能のほかに、連絡先プロバイダには 次の便利な機能があります。

  • 連絡先グループ
  • 写真の機能

連絡先グループ

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

サーバーからの未加工連絡先データを連絡先プロバイダに追加する同期アダプタを設計している場合、グループを使用しないのであれば、連絡先プロバイダに対してデータが可視であることを通知する必要があります。ユーザーがアカウントを追加したときに実行されるコード ContactsContract.Settings を更新する 連絡先プロバイダがアカウントに追加した行です。この行で、変数の値を Settings.UNGROUPED_VISIBLE 列を 1 にします。こうすると、連絡先プロバイダは、グループを使用していない場合でも、独自の連絡先データを常に表示します。

連絡先の写真

ContactsContract.Data テーブルには、写真が MIME タイプの行として保存されます Photo.CONTENT_ITEM_TYPE。行の CONTACT_ID 列は、 それが属する未加工連絡先の _ID 列。 クラス ContactsContract.Contacts.Photo は、連絡先のプライマリ フォトの写真情報を格納する ContactsContract.Contacts のサブテーブルを定義します。ここでプライマリ フォトとは、連絡先のプライマリ未加工連絡先のプライマリ フォトのことを示します。同様に、クラス ContactsContract.RawContacts.DisplayPhoto は、未加工連絡先のプライマリ フォトの写真情報を格納する ContactsContract.RawContacts のサブテーブルを定義します。

ContactsContract.Contacts.Photo とリファレンス ドキュメント ContactsContract.RawContacts.DisplayPhoto に含まれるものの例 取得しています。プライマリを取得するためのコンビニエンス クラスがない 未加工連絡先のサムネイルが表示されますが、 ContactsContract.Data テーブル。未加工連絡先の _IDPhoto.CONTENT_ITEM_TYPEIS_PRIMARY 未加工連絡先のプライマリ写真行を検索できます。

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