カレンダー プロバイダ

カレンダー プロバイダは、ユーザーのカレンダー イベントのためのリポジトリです。Calendar Provider API を使用すると、カレンダー、イベント、参加者、リマインダーなどに対するクエリ、挿入、アップデート、削除の操作を実行できます。

Calendar Provider API は、アプリケーションや同期アダプタで使用できます。ルールは、呼び出しを実行しているプログラムのタイプによって異なります。 本書では主に、Calendar Provider API のアプリケーションとしての使用について説明します。 同期アダプタの場合の違いについては、同期アダプタをご覧ください。

通常、カレンダー データを読み取ったり書き込んだりするためには、アプリケーションのマニフェストにユーザー パーミッションで説明している適切なパーミッションが含まれている必要があります。 一般的な操作を簡単に実行できるようにするため、カレンダー プロバイダには、いくつかのインテントが用意されています。これらについては、カレンダーのインテントで説明しています。 これらのインテントは、ユーザーをカレンダー アプリケーションに誘導して、イベントを挿入、参照、編集できるようにします。 ユーザーは、カレンダー アプリケーションを操作した後、元のアプリケーションに戻ります。 これにより、アプリケーションがパーミッションを要求したり、イベントの参照や作成のためのインターフェースを提供したりする必要がなくなります。

基本

コンテンツ プロバイダは、データを保存してアプリケーションからアクセスできるようにします。 Android プラットフォームによって提供されるコンテンツ プロバイダ(カレンダー プロバイダを含む)は、一般にリレーショナル データベース モデルに基づくテーブルのセットとしてデータを公開します。このモデルにおいて、各行はレコード、各列は特定のタイプと意味のデータになっています。 アプリケーションと同期アダプタは、ユーザーのカレンダー データを保持するデータベース テーブルの読み取りおよび書き込みアクセスを、Calendar Provider API を使用して取得できます。

各コンテンツ プロバイダは、データセットを一意に指定するパブリック URI( Uri オブジェクトとしてラップされています)を公開します。 複数のデータセット(複数のテーブル)を制御するコンテンツ プロバイダは、データセットごとに個別の URI を公開します。 プロバイダの URI はすべて、文字列「content://」で始まります。 これは、コンテンツ プロバイダによって管理されているデータであることを示します。 カレンダー プロバイダは、クラス(テーブル)それぞれの URI のための定数を定義します。 URI の形式は <class>.CONTENT_URI です。 たとえば、Events.CONTENT_URI のようになります。

図 1 に、カレンダー プロバイダのデータモデルを図示します。メイン テーブルとそれらを相互にリンクしているフィールドが示されています。

カレンダー プロバイダのデータモデル

図1. カレンダー プロバイダのデータモデル

ユーザーは複数のカレンダーを持つことができ、カレンダーをそれぞれ異なるタイプのアカウント(Google カレンダー、Exchange など)と関連付けることができます。

CalendarContract は、カレンダーのデータモデルとイベント関連の情報を定義します。データは、次に示すいくつかのテーブルに保存されます。

テーブル(クラス) 説明

CalendarContract.Calendars

このテーブルは、該当するカレンダー固有の情報を保持します。 各行には、名前、色、同期情報など、1 つのカレンダーに関する詳細が格納されます。
CalendarContract.Eventsこのテーブルは、該当するイベント固有の情報を保持します。 各行には、1 つのイベントに関する情報が格納されます—イベントのタイトル、場所、開始時刻、終了時刻などが該当します。 イベントは、単発だったり繰り返し発生したりすることが考えられます。参加者、リマインダー、拡張プロパティは、個別のテーブルに格納されます。これらのテーブルにはそれぞれ、Events テーブルの_ID を参照するEVENT_ID があります。
CalendarContract.Instancesこのテーブルは、それぞれの発生イベントの開始時刻と終了時刻を保持します。 各行は、イベントの発生 1 回を表します。 単発イベントには、イベントのインスタンスが 1 対 1 で対応します。 繰り返しのイベントには、そのイベントの複数回の発生それぞれに対応する複数の行が自動生成されます。
CalendarContract.Attendeesこのテーブルは、イベントの参加者(ゲスト)の情報を保持します。 各行は、1 回のイベントの 1 人のゲストを表します。 ゲストのタイプと、ゲストのイベントへの出欠の回答を指定します。
CalendarContract.Remindersこのテーブルは、アラートや通知のデータを保持します。 各行は、1 回のイベントの 1 つのアラートを表します。1 つのイベントが複数のリマインダーを持つことができます。 1 イベントあたりの最大リマインダー数は MAX_REMINDERS で指定され、指定されたカレンダーを所有する同期アダプタによって設定されます。 リマインダーは、イベント開始前の分単位で指定され、ユーザーに注意喚起する手段を示す情報を保持します。

Calendar Provider API は、柔軟で効果的な設計になっています。ですが同時に、エンドユーザーにとって使いやすくすることや、カレンダーとそのデータの整合性を保護することが重要です。 次に、そのために API の使用に際して留意すべきことを示します。

  • カレンダー イベントの挿入、アップデート、参照カレンダー プロバイダからイベントの挿入、変更、読み取りを直接実行するためには、適切なパーミッションが必要です。ただし、作成しているのが本格的なカレンダー アプリケーションや同期アダプタではない場合は、そうしたパーミッションを要求する必要はありません。その代わりに、Android のカレンダー アプリケーションがサポートしているインテントを使用し、読み取りと書き込みの操作をそのアプリケーションに引き渡すことができます。インテントを使用すると、アプリケーションがユーザーをカレンダー アプリケーションに誘導し、自動入力されたフォームで目的の操作を実行するようになります。 操作を完了したユーザーは、アプリケーションに戻されます。一般的な操作をカレンダーを介して実行するようにアプリケーションを設計することで、ユーザーに一貫性のある堅牢なユーザー インターフェースが提供できます。 これが推奨アプローチです。 詳細については、カレンダーのインテントをご覧ください。

  • 同期アダプタ。同期アダプタは、ユーザーの端末上にあるカレンダー データを別のサーバーやデータソースと同期します。 CalendarContract.Calendars テーブルと CalendarContract.Events テーブルには、同期アダプタ用に予約された列があります。プロバイダやアプリケーションがそれらを変更しないようにする必要があります。 実際には、同期アダプタとしてアクセスしない限り、それらの列は参照できません。 同期アダプタの詳細については、同期アダプタをご覧ください。

ユーザー パーミッション

カレンダー データを読み込むためには、アプリケーションはマニフェスト ファイルに READ_CALENDAR パーミッションを含める必要があります。カレンダー データを削除、挿入、アップデートするためには、WRITE_CALENDAR パーミッションを含める必要があります。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    ...
</manifest>

Calendars テーブル

CalendarContract.Calendars テーブルには、個々のカレンダーの詳細情報が格納されます。 次に示す Calendars の列は、アプリケーションと同期アダプタのどちらからも書き込み可能です。サポートされているすべてのフィールドの一覧については、 CalendarContract.Calendars のリファレンスをご覧ください。

定数 説明
NAMEカレンダーの名前。
CALENDAR_DISPLAY_NAMEユーザーに表示されるこのカレンダーの名前。
VISIBLEカレンダーが表示対象として選択されているかどうかを示すブール値。 値 0 は、このカレンダーに関連付けられているイベントを表示しないことを示します。 値 1 は、このカレンダーに関連付けられているイベントを表示することを示します。 この値は、CalendarContract.Instances テーブルの行の生成に影響を与えます。
SYNC_EVENTSカレンダーを同期してそのイベントを端末に格納するかどうかを示すブール値。 値 0 は、このカレンダーを同期せず、イベントを端末に格納しないことを指定します。 値 1 は、このカレンダーのイベントを同期し、イベントを端末に格納することを指定します。

カレンダーへのクエリ

次の例は、特定のユーザーが所有するカレンダーを取得する方法を示しています。 簡潔にするため、この例のクエリ操作はユーザー インターフェース スレッド(「メインスレッド」)内に示されています。 実際には、メインスレッドではなく非同期スレッドで実行してください。 詳細については、ローダをご覧ください。 データを読み込むだけでなく変更する場合は、AsyncQueryHandler をご覧ください。

// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
    Calendars._ID,                           // 0
    Calendars.ACCOUNT_NAME,                  // 1
    Calendars.CALENDAR_DISPLAY_NAME,         // 2
    Calendars.OWNER_ACCOUNT                  // 3
};

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;

この例の次の部分では、クエリを作成します。クエリの条件は selection に指定しています。 この例のクエリは、ACCOUNT_NAME が「sampleuser@google.com」で、ACCOUNT_TYPE が「com.google」で、OWNER_ACCOUNT が「sampleuser@google.com」であるカレンダーを検索します。 ユーザーが所有しているだけでなく参照したことのあるすべてのカレンダーを確認するには、OWNER_ACCOUNT を省略します。このクエリは、データベース クエリが返した結果セットをトラバースするのに使用できる Cursor オブジェクトを返します。 コンテンツ プロバイダでのクエリの使用については、コンテンツ プロバイダをご覧ください。

// Run query
Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
                        + Calendars.OWNER_ACCOUNT + " = ?))";
String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google",
        "sampleuser@gmail.com"};
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);

次のセクションでは、カーソルを使用して結果セットをステップ単位で移動します。例の冒頭で設定した定数を使用して各フィールドの値を返します。

// Use the cursor to step through the returned records
while (cur.moveToNext()) {
    long calID = 0;
    String displayName = null;
    String accountName = null;
    String ownerName = null;

    // Get the field values
    calID = cur.getLong(PROJECTION_ID_INDEX);
    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);

    // Do something with the values...

   ...
}

カレンダーの変更

カレンダーのアップデートを実行する場合、カレンダーの _ID を、URI(withAppendedId())に付与する ID として、または最初の selection 項目として指定できます。 先ほどの selectionを "_id=?" で始め、最初の selectionArg をカレンダーの _ID にする必要があります。URI に含まれる ID をエンコードすることでも、アップデートを実行できます。 この例では、カレンダーの表示名をこの(withAppendedId() を使用する)アプローチで変更しています。

private static final String DEBUG_TAG = "MyActivity";
...
long calID = 2;
ContentValues values = new ContentValues();
// The new display name for the calendar
values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);

カレンダーの挿入

カレンダーは主に同期アダプタで管理するように設計されているため、新しいカレンダーは必ず同期アダプタとして挿入してください。 ほとんどの場合、アプリケーションはカレンダーに対し、表示名の変更のような表面的な変更しかできません。 アプリケーションがローカル カレンダーを作成する必要がある場合は、ACCOUNT_TYPE_LOCALACCOUNT_TYPE を使用し、カレンダーの挿入を同期アダプタとして実行します。 ACCOUNT_TYPE_LOCAL は、端末アカウントと関連付けられていないカレンダー用の特殊なアカウント タイプです。 このタイプのカレンダーは、サーバーと同期されません。同期アダプタの詳細については、同期アダプタをご覧ください。

Events テーブル

CalendarContract.Events テーブルには、個々のイベントの詳細情報が格納されます。 イベントを追加、アップデート、削除するためには、アプリケーションはマニフェスト ファイルWRITE_CALENDAR パーミッションを含める必要があります。

次に示す Events の列は、アプリケーションと同期アダプタのどちらからも書き込み可能です。 サポートされているすべてのフィールドの一覧については、CalendarContract.Events のリファレンスをご覧ください。

定数 説明
CALENDAR_IDイベントが属するカレンダーの _ID
ORGANIZER イベントの主催者(所有者)のメールアドレス。
TITLEイベントのタイトル。
EVENT_LOCATIONイベントが開催される場所。
DESCRIPTIONイベントの説明。
DTSTART エポックからの UTC ミリ秒単位で表現されたイベント開始時刻。
DTENDエポックからの UTC ミリ秒単位で表現されたイベント終了時刻。
EVENT_TIMEZONEイベントのタイムゾーン。
EVENT_END_TIMEZONEイベントの終了時刻のタイムゾーン。
DURATIONRFC5545 形式でのイベントの期間。たとえば、"PT1H" という値はイベントが 1 時間であること、"P2W" という値は期間が 2 週間であることを示します。
ALL_DAY値 1 は、そのイベントがローカルのタイムゾーンで定義された終日を占めることを示します。 値 0 は、1 日のどこかで始まって終わる定期のイベントであることを示します。
RRULEイベント形式の繰り返し発生ルール。 たとえば、"FREQ=WEEKLY;COUNT=10;WKST=SU" のようになります。 その他の例については、こちらをご覧ください。
RDATEイベントの繰り返し発生日。 通常、RDATERRULE と組み合わせて、繰り返し発生についてまとめて定義するのに使用します。 詳細については、RFC5545 の仕様をご覧ください。
AVAILABILITYこのイベントの時間を予定が入っている時間とみなすか、調整可能な空き時間とみなすか。
GUESTS_CAN_MODIFYゲストがイベントを変更できるかどうか。
GUESTS_CAN_INVITE_OTHERS ゲストが他のゲストを招待できるかどうか。
GUESTS_CAN_SEE_GUESTSゲストが他の参加者のリストを参照できるかどうか。

イベントの追加

アプリケーションが新しいイベントを挿入する場合は、INSERT インテントを使用することをお勧めします。詳細については、インテントを使用したイベントの挿入をご覧ください。 ただし、必要があればイベントを直接挿入できます。 ここではその方法について説明します。

新しいイベントを挿入するためのルールは次のとおりです。

  • CALENDAR_IDDTSTART を含める必要があります。
  • EVENT_TIMEZONE を含める必要があります。システムにインストールされているタイムゾーン ID のリストを取得するには、getAvailableIDs() を使用します。 インテントを使用したイベントの挿入で説明しているように、INSERT インテントを使用してイベントを挿入している場合、このルールは該当しません。その場合はデフォルトのタイムゾーンが指定されます。
  • 繰り返されないイベントには、DTEND を含める必要があります。
  • 繰り返されるイベントには RRULERDATE に加えてDURATION を含める必要があります。インテントを使用したイベントの挿入で説明しているように、INSERT インテントを使用してイベントを挿入している場合、このルールは該当しません。その場合は RRULEDTENDDTSTART と組み合わせて使用でき、カレンダー アプリケーションはこれを自動で期間に変換します。

イベントの挿入の例を次に示します。簡潔にするため、この例は UI スレッドで実行されています。 実際には、挿入やアップデートは非同期スレッドで実行して、アクションをバックグラウンド スレッドに移してください。 詳細については、AsyncQueryHandler をご覧ください。

long calID = 3;
long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 9, 14, 7, 30);
startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 9, 14, 8, 45);
endMillis = endTime.getTimeInMillis();
...

ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, startMillis);
values.put(Events.DTEND, endMillis);
values.put(Events.TITLE, "Jazzercise");
values.put(Events.DESCRIPTION, "Group workout");
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
Uri uri = cr.insert(Events.CONTENT_URI, values);

// get the event ID that is the last element in the Uri
long eventID = Long.parseLong(uri.getLastPathSegment());
//
// ... do something with event ID
//
//

注: イベントの作成後にイベント ID を取得していることに注目してください。 これがイベント ID を取得する最も簡単な方法です。他のカレンダー操作を実行するためにイベント ID が必要になることはよくあります。たとえば、イベントに対する参加者やリマインダーの追加などが該当します。

イベントのアップデート

アプリケーションでユーザーによるイベントの編集を許可する場合は、EDIT インテントを使用することをお勧めします。詳細については、インテントを使用したイベントの挿入をご覧ください。ただし、イベントは必要に応じて直接編集できます。 イベントのアップデートを実行する場合は、イベントの _ID を、URI(withAppendedId())に追加された ID として、または前出の selection の最初の項目として指定できます。前出の selection を "_id=?" で始め、最初の selectionArg をイベントの _ID にする必要があります。 ID なしの selection を使用してアップデートすることもできます。 次に、イベントのアップデートの例を示します。 ここでは、withAppendedId() を使用するアプローチでイベントのタイトルを変更しています。

private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 188;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri updateUri = null;
// The new title for the event
values.put(Events.TITLE, "Kickboxing");
updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);  

イベントの削除

イベントは、URI に追加した ID である _ID によって、または標準的な選択によって削除できます。 ID の追加で削除を実行する場合、選択はできません。削除には、アプリケーションとして実行する方法と同期アダプタとして実行する方法の 2 種類があります。 アプリケーションによる削除では、削除される列が 1 に設定されます。このフラグは同期アダプタに対して、その行が削除されたことを通知し、この削除をサーバーに通知するよう指示します。 同期アダプタとしての削除では、すべての関連データとともにデータベースからイベントが削除されます。 次の例では、アプリケーションがイベントをその _ID を使用して削除しています。

private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 201;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri deleteUri = null;
deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().delete(deleteUri, null, null);
Log.i(DEBUG_TAG, "Rows deleted: " + rows);

Attendees テーブル

CalendarContract.Attendees テーブルの各行は、あるイベントの 1 人の参加者ないしゲストを表します。 query() を呼び出すと、指定された EVENT_ID を持つイベントの参加者リストが返されます。この EVENT_ID は特定のイベントの _ID と一致している必要があります。

次の表に、書き込み可能なフィールドを示します。 新しい参加者を挿入する際には、ATTENDEE_NAME を除くすべてを含める必要があります。

定数 説明
EVENT_IDイベントの ID。
ATTENDEE_NAME参加者の名前。
ATTENDEE_EMAIL参加者のメールアドレス。
ATTENDEE_RELATIONSHIP

参加者のイベントに対する関係。次のどれかです。

ATTENDEE_TYPE

参加者のタイプ。次のどれかです。

ATTENDEE_STATUS

参加者の出席ステータス。次のどれかです。

参加者の追加

次の例では、あるイベントに 1 人の参加者を挿入しています。EVENT_ID は必須です。

long eventID = 202;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Attendees.ATTENDEE_NAME, "Trevor");
values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
values.put(Attendees.EVENT_ID, eventID);
Uri uri = cr.insert(Attendees.CONTENT_URI, values);

Reminders テーブル

CalendarContract.Reminders テーブルの各行は、あるイベントの 1 つのリマインダーを表します。 query() を呼び出すと、指定された EVENT_ID を持つイベントのリマインダー リストが返されます。

次の表に、リマインダーに関する書き込み可能なフィールドを示します。新しいリマインダーを挿入する際には、これらをすべて含める必要があります。 同期アダプタは、サポートするリマインダーのタイプを CalendarContract.Calendars テーブルで指定しています。 詳細については、 ALLOWED_REMINDERS をご覧ください。

定数 説明
EVENT_IDイベントの ID。
MINUTESリマインダーが通知されるまでの分単位の時間。
METHOD

アラーム手段。サーバーに設定されます。次のどれかです。

リマインダーの追加

この例では、イベントにリマインダーを追加します。このリマインダーは、イベントの 15 分前に通知されます。

long eventID = 221;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Reminders.MINUTES, 15);
values.put(Reminders.EVENT_ID, eventID);
values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
Uri uri = cr.insert(Reminders.CONTENT_URI, values);

Instances テーブル

CalendarContract.Instances テーブルは、あるイベントの発生の開始時刻と終了時刻を保持します。 各行は、イベントの発生 1 回を表します。 Instances テーブルは書き込みできません。イベントの発生をクエリする手段を提供するためだけのものです。

次の表に、インスタンスに関してクエリできるフィールドの一部を示します。タイムゾーンは KEY_TIMEZONE_TYPEKEY_TIMEZONE_INSTANCES で定義されます。

定数 説明
BEGINインスタンスの開始時刻(UTC ミリ秒単位)。
ENDインスタンスの終了時刻(UTC ミリ秒単位)。
END_DAYカレンダーのタイムゾーンにおけるユリウス暦でのインスタンスの終了日。
END_MINUTEカレンダーのタイムゾーンにおける午前零時を基準にしたインスタンスの終了分。
EVENT_ID このインスタンスのイベントの _ID
START_DAYカレンダーのタイムゾーンにおけるユリウス暦でのインスタンスの開始日。
START_MINUTEカレンダーのタイムゾーンにおける午前零時を基準にしたインスタンスの開始分。

Instances テーブルへのクエリ

Instances テーブルにクエリするには、クエリの範囲を示す時刻を URI に指定する必要があります。 この例で、CalendarContract.InstancesCalendarContract.EventsColumns インターフェースの実装を介して TITLE フィールドへのアクセスを取得します。言い換えると、TITLE はデータベース ビューによって返されているのであって、CalendarContract.Instances テーブルそのものへのクエリによって返されているのではありません。

private static final String DEBUG_TAG = "MyActivity";
public static final String[] INSTANCE_PROJECTION = new String[] {
    Instances.EVENT_ID,      // 0
    Instances.BEGIN,         // 1
    Instances.TITLE          // 2
  };

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_BEGIN_INDEX = 1;
private static final int PROJECTION_TITLE_INDEX = 2;
...

// Specify the date range you want to search for recurring
// event instances
Calendar beginTime = Calendar.getInstance();
beginTime.set(2011, 9, 23, 8, 0);
long startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2011, 10, 24, 8, 0);
long endMillis = endTime.getTimeInMillis();

Cursor cur = null;
ContentResolver cr = getContentResolver();

// The ID of the recurring event whose instances you are searching
// for in the Instances table
String selection = Instances.EVENT_ID + " = ?";
String[] selectionArgs = new String[] {"207"};

// Construct the query with the desired date range.
Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);

// Submit the query
cur =  cr.query(builder.build(),
    INSTANCE_PROJECTION,
    selection,
    selectionArgs,
    null);

while (cur.moveToNext()) {
    String title = null;
    long eventID = 0;
    long beginVal = 0;

    // Get the field values
    eventID = cur.getLong(PROJECTION_ID_INDEX);
    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
    title = cur.getString(PROJECTION_TITLE_INDEX);

    // Do something with the values.
    Log.i(DEBUG_TAG, "Event:  " + title);
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(beginVal);
    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));
    }
 }

カレンダーのインテント

アプリケーションがカレンダー データの読み取りや書き込みを実行するのにパーミッションは要りません。その代わり、Android のカレンダー アプリケーションがサポートするインテントを使用し、読み取りと書き込みの操作をそのアプリケーションに引き渡します。次の表に、カレンダー プロバイダによってサポートされているインテントを示します。

アクション URI 説明 エクストラ

VIEW

content://com.android.calendar/time/<ms_since_epoch>

URI は CalendarContract.CONTENT_URI を使用しても参照できます。このインテントの使用例については、インテントを使用したカレンダー データの参照をご覧ください。
<ms_since_epoch> に指定された時刻にカレンダーを開きます。 なし。

VIEW

content://com.android.calendar/events/<event_id>

URI は Events.CONTENT_URI を使用しても参照できます。このインテントの使用例については、インテントを使用したカレンダー データの参照をご覧ください。
<event_id>で指定されたイベントを参照します。CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

content://com.android.calendar/events/<event_id>

URI は Events.CONTENT_URI を使用しても参照できます。このインテントの使用例については、インテントを使用したカレンダー データの編集をご覧ください。
<event_id>で指定されたイベントを編集します。CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

URI は Events.CONTENT_URI を使用しても参照できます。このインテントの使用例については、インテントを使用したカレンダー データの挿入をご覧ください。
イベントを作成します。 次の表に示す任意のエクストラ。

次の表に、カレンダー プロバイダによってサポートされているインテント エクストラを示します。

インテント エクストラ 説明
Events.TITLEイベントの名前。
CalendarContract.EXTRA_EVENT_BEGIN_TIMEイベントの開始時刻(エポックからのミリ秒単位)。
CalendarContract.EXTRA_EVENT_END_TIMEイベントの終了時刻(エポックからのミリ秒単位)。
CalendarContract.EXTRA_EVENT_ALL_DAYイベントが終日かどうかを示すブール値。 使用できる値は true または false です。
Events.EVENT_LOCATIONイベントの場所。
Events.DESCRIPTIONイベントの説明。
Intent.EXTRA_EMAIL招待者のメールアドレス(コンマ区切りリスト形式)。
Events.RRULEイベントの繰り返しルール。
Events.ACCESS_LEVELイベントが公開か非公開か。
Events.AVAILABILITYこのイベントの時間を予定が入っている時間とみなすか、調整可能な空き時間とみなすか。

次のセクションでは、これらのインテント使用方法を説明します。

インテントを使用したイベントの挿入

INSERT インテントを使用すると、アプリケーションはイベント挿入タスクをカレンダーそのものに引き渡すことができます。このアプローチでは、アプリケーションは WRITE_CALENDAR パーミッションをマニフェスト ファイルに含める必要すらありません。

このアプローチのアプリケーションをユーザーが実行すると、アプリケーションによってユーザーがカレンダーに誘導されイベントを追加できるようになります。 INSERT インテントは、追加フィールドを使用して、カレンダーに格納されている詳細情報をフォームに自動入力します。 それに対して、ユーザーはイベントのキャンセル、必要に応じたイベントの編集、イベントのカレンダーへの保存ができます。

次に示すのは、2012 年 1 月 19 日の午前 7:30 から 午前 8:30 までのイベントをスケジュールするコード スニペットです。このコード スニペットの以下の点に注目してください。

Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 0, 19, 7, 30);
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 0, 19, 8, 30);
Intent intent = new Intent(Intent.ACTION_INSERT)
        .setData(Events.CONTENT_URI)
        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
        .putExtra(Events.TITLE, "Yoga")
        .putExtra(Events.DESCRIPTION, "Group class")
        .putExtra(Events.EVENT_LOCATION, "The gym")
        .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
startActivity(intent);

インテントを使用したイベントの編集

イベントは、イベントのアップデートで説明したように、直接アップデートできます。ただし、EDIT インテントを使用すれば、パーミッションのないアプリケーションが、カレンダー アプリケーションにイベント編集を引き渡すことができます。カレンダーでのイベント編集を終えたユーザーは、元のアプリケーションに戻ります。

次の例のインテントは、指定されたイベントの新しいタイトルを設定して、ユーザーがそのイベントをカレンダーで編集できるようにしています。

long eventID = 208;
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_EDIT)
    .setData(uri)
    .putExtra(Events.TITLE, "My New Title");
startActivity(intent);

インテントを使用したカレンダー データの参照

カレンダー プロバイダには、VIEW インテントを使用する次の 2 つの方法が用意されています。

  • 特定の日付のカレンダーを開く方法。
  • イベントを参照する方法。

次の例は、特定の日付のカレンダーを開く方法を示しています。

// A date-time specified in milliseconds since the epoch.
long startMillis;
...
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
ContentUris.appendId(builder, startMillis);
Intent intent = new Intent(Intent.ACTION_VIEW)
    .setData(builder.build());
startActivity(intent);

次の例は、参照するためにイベントを開く方法を示しています。

long eventID = 208;
...
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_VIEW)
   .setData(uri);
startActivity(intent);

同期アダプタ

アプリケーションと同期アダプタとの間で、カレンダー プロバイダにアクセスする方法の違いは、次のいくつかだけです。

  • 同期アダプタは、CALLER_IS_SYNCADAPTERtrue に設定して、それが同期アダプタであることを示す必要があります。
  • 同期アダプタは、ACCOUNT_NAMEACCOUNT_TYPE をクエリ パラメータとして URI に指定する必要があります。
  • 同期アダプタは、アプリケーションやウィジェットより多くの列に書き込みアクセスできます。 たとえば、アプリケーションで変更できるのは、名前、表示名、可視性の設定、カレンダーが同期対象かどうかなど、カレンダーの特性の一部のみです。 それに対し、同期アダプタはこれらの列の他に、カレンダーの色、タイムゾーン、アクセスレベル、場所など、多くのものにアクセスできます。ただし、指定した ACCOUNT_NAMEACCOUNT_TYPE に限定されます。

次に示すヘルパー メソッドを使用すると、同期アダプタで使用するための URI が返されます。

 static Uri asSyncAdapter(Uri uri, String account, String accountType) {
    return uri.buildUpon()
        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
 }

同期アダプタの実装例(カレンダーに特化したものではありません)については、SampleSyncAdapter をご覧ください。