カレンダー プロバイダの概要

カレンダー プロバイダは、ユーザーのカレンダーの予定のリポジトリです。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 つのイベントに関する情報(イベントのタイトル、場所、開始時刻、終了時刻など)が含まれています。イベントは 1 回限り発生することも、複数回発生することもあります。参加者、リマインダー、拡張プロパティは、別々のテーブルに保存されます。それぞれに、Events テーブルの _ID を参照する EVENT_ID があります。
CalendarContract.Instances このテーブルには、イベントの各発生の開始時刻と終了時刻が格納されます。このテーブルの各行は、1 つのイベントの発生を表します。1 回限りのイベントの場合、インスタンスとイベントは 1 対 1 でマッピングされます。定期的な予定の場合、その予定の複数の発生に対応する複数の行が自動的に生成されます。
CalendarContract.Attendees このテーブルには、予定の参加者(ゲスト)の情報が格納されます。各行は、予定の 1 人のゲストを表します。ゲストのタイプと、イベントに対するゲストの出欠確認の回答を指定します。
CalendarContract.Reminders このテーブルには、アラート / 通知データが格納されます。各行は、イベントに対する 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>

カレンダー テーブル

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

定数 説明
NAME カレンダーの名前。
CALENDAR_DISPLAY_NAME ユーザーに表示されるこのカレンダーの名前。
VISIBLE カレンダーが表示対象として選択されているかどうかを示すブール値。値が 0 の場合、このカレンダーに関連付けられている予定は表示されません。値が 1 の場合、このカレンダーに関連付けられている予定が表示されます。この値は、CalendarContract.Instances テーブルの行の生成に影響します。
SYNC_EVENTS カレンダーを同期して、その予定をデバイスに保存する必要があるかどうかを示すブール値。値が 0 の場合、このカレンダーを同期せず、その予定をデバイスに保存しません。値が 1 の場合、このカレンダーの予定を同期し、その予定をデバイスに保存します。

すべてのオペレーションにアカウントの種類を含める

Calendars.ACCOUNT_NAME をクエリする場合は、選択に Calendars.ACCOUNT_TYPE も含める必要があります。これは、特定のアカウントは、ACCOUNT_NAME と ACCOUNT_TYPE の両方が指定されている場合にのみ一意と見なされるためです。ACCOUNT_TYPE は、アカウントが AccountManager に登録されたときに使用されたアカウント認証システムに対応する文字列です。また、デバイス アカウントに関連付けられていないカレンダー用に、ACCOUNT_TYPE_LOCAL という特別なタイプのアカウントもあります。ACCOUNT_TYPE_LOCAL アカウントは同期されません。

カレンダーをクエリする

次の例は、特定のユーザーが所有するカレンダーを取得する方法を示しています。この例では、わかりやすくするために、クエリ オペレーションをユーザー インターフェース スレッド(「メインスレッド」)に表示しています。実際には、これはメインスレッドではなく非同期スレッドで行う必要があります。詳細については、ローダをご覧ください。データの読み取りだけでなく変更も行う場合は、AsyncQueryHandler をご覧ください。

Kotlin

// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
private val EVENT_PROJECTION: Array<String> = arrayOf(
        CalendarContract.Calendars._ID,                     // 0
        CalendarContract.Calendars.ACCOUNT_NAME,            // 1
        CalendarContract.Calendars.CALENDAR_DISPLAY_NAME,   // 2
        CalendarContract.Calendars.OWNER_ACCOUNT            // 3
)

// The indices for the projection array above.
private const val PROJECTION_ID_INDEX: Int = 0
private const val PROJECTION_ACCOUNT_NAME_INDEX: Int = 1
private const val PROJECTION_DISPLAY_NAME_INDEX: Int = 2
private const val PROJECTION_OWNER_ACCOUNT_INDEX: Int = 3

Java

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

この例の次の部分では、クエリを作成します。この選択により、クエリの条件が指定されます。この例では、ACCOUNT_NAME が「hera@example.com」、ACCOUNT_TYPE が「com.example」、OWNER_ACCOUNT が「hera@example.com」のカレンダーを検索しています。ユーザーが所有しているカレンダーだけでなく、ユーザーが閲覧したすべてのカレンダーを表示する場合は、OWNER_ACCOUNT を省略します。クエリは Cursor オブジェクトを返します。このオブジェクトを使用して、データベース クエリから返された結果セットを走査できます。コンテンツ プロバイダでのクエリの使用について詳しくは、コンテンツ プロバイダをご覧ください。

Kotlin

// Run query
val uri: Uri = CalendarContract.Calendars.CONTENT_URI
val selection: String = "((${CalendarContract.Calendars.ACCOUNT_NAME} = ?) AND (" +
        "${CalendarContract.Calendars.ACCOUNT_TYPE} = ?) AND (" +
        "${CalendarContract.Calendars.OWNER_ACCOUNT} = ?))"
val selectionArgs: Array<String> = arrayOf("hera@example.com", "com.example", "hera@example.com")
val cur: Cursor = contentResolver.query(uri, EVENT_PROJECTION, selection, selectionArgs, null)

Java

// 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[] {"hera@example.com", "com.example",
        "hera@example.com"};
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);

次のセクションでは、カーソルを使用して結果セットをステップ実行します。このメソッドは、サンプルの最初に設定された定数を使用して、各フィールドの値を返します。

Kotlin

// Use the cursor to step through the returned records
while (cur.moveToNext()) {
    // Get the field values
    val calID: Long = cur.getLong(PROJECTION_ID_INDEX)
    val displayName: String = cur.getString(PROJECTION_DISPLAY_NAME_INDEX)
    val accountName: String = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX)
    val ownerName: String = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX)
    // Do something with the values...
}

Java

// 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 に追加された ID(withAppendedId())として、または最初の選択項目として指定します。選択は "_id=?" で始まり、最初の selectionArg はカレンダーの _ID である必要があります。URI で ID をエンコードして更新することもできます。この例では、(withAppendedId()) アプローチを使用してカレンダーの表示名を変更します。

Kotlin

const val DEBUG_TAG: String = "MyActivity"
...
val calID: Long = 2
val values = ContentValues().apply {
    // The new display name for the calendar
    put(CalendarContract.Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar")
}
val updateUri: Uri = ContentUris.withAppendedId(CalendarContract.Calendars.CONTENT_URI, calID)
val rows: Int = contentResolver.update(updateUri, values, null, null)
Log.i(DEBUG_TAG, "Rows updated: $rows")

Java

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_LOCAL の ACCOUNT_TYPE を使用して、同期アダプターとしてカレンダーの挿入を実行することで、ローカル カレンダーを作成できます。ACCOUNT_TYPE_LOCAL は、デバイス アカウントに関連付けられていないカレンダー用の特別なアカウント タイプです。このタイプのカレンダーはサーバーと同期されません。同期アダプターの詳細については、同期アダプターをご覧ください。

イベント テーブル

CalendarContract.Events テーブルには、個々のイベントの詳細が含まれています。予定を追加、更新、削除するには、アプリのマニフェスト ファイルに WRITE_CALENDAR 権限を含める必要があります。

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

定数 説明
CALENDAR_ID 予定が属するカレンダーの _ID。
ORGANIZER 予定の主催者(オーナー)のメールアドレス。
TITLE 予定のタイトル。
EVENT_LOCATION イベントが開催される場所。
DESCRIPTION イベントの説明。
DTSTART 予定の開始時刻(エポックからの UTC ミリ秒)。
DTEND 予定の終了時間(エポックからの UTC ミリ秒)。
EVENT_TIMEZONE 予定のタイムゾーン。
EVENT_END_TIMEZONE 予定の終了時刻のタイムゾーン。
DURATION イベントの所要時間(RFC5545 形式)。たとえば、値 "PT1H" はイベントが 1 時間続くことを示し、値 "P2W" は 2 週間続くことを示します。
ALL_DAY 値が 1 の場合、このイベントは現地タイムゾーンで定義された 1 日全体を占めます。値が 0 の場合は、1 日の任意の時間に開始および終了する通常のイベントであることを示します。
RRULE 予定の形式の繰り返しルール。たとえば、"FREQ=WEEKLY;COUNT=10;WKST=SU"。その他の例については、RFC5545 仕様をご覧ください。
RDATE イベントの繰り返し日。 通常、RDATE は RRULE と組み合わせて使用し、繰り返されるオカレンスの集計セットを定義します。詳しくは、RFC5545 仕様をご覧ください。
AVAILABILITY この予定が予定ありの時間としてカウントされるか、予定を上書きできる予定なしの時間であるか。
GUESTS_CAN_MODIFY ゲストが予定を変更できるかどうか。
GUESTS_CAN_INVITE_OTHERS ゲストが他のゲストを招待できるかどうか。
GUESTS_CAN_SEE_GUESTS ゲストが参加者のリストを表示できるかどうか。

予定を追加

アプリケーションで新しい予定を挿入する場合は、インテントを使用して予定を挿入するで説明されているように、INSERT インテントを使用することをおすすめします。ただし、必要に応じて、イベントを直接挿入することもできます。このセクションでは、その方法について説明します。

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

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

以下に、イベントを挿入する例を示します。簡素化のため、これは UI スレッドで実行されています。実際には、挿入と更新は非同期スレッドで行い、アクションをバックグラウンド スレッドに移動する必要があります。詳細については、AsyncQueryHandler をご覧ください。

Kotlin

val calID: Long = 3
val startMillis: Long = Calendar.getInstance().run {
    set(2012, 9, 14, 7, 30)
    timeInMillis
}
val endMillis: Long = Calendar.getInstance().run {
    set(2012, 9, 14, 8, 45)
    timeInMillis
}
...

val values = ContentValues().apply {
    put(CalendarContract.Events.DTSTART, startMillis)
    put(CalendarContract.Events.DTEND, endMillis)
    put(CalendarContract.Events.TITLE, "Jazzercise")
    put(CalendarContract.Events.DESCRIPTION, "Group workout")
    put(CalendarContract.Events.CALENDAR_ID, calID)
    put(CalendarContract.Events.EVENT_TIMEZONE, "America/Los_Angeles")
}
val uri: Uri = contentResolver.insert(CalendarContract.Events.CONTENT_URI, values)

// get the event ID that is the last element in the Uri
val eventID: Long = uri.lastPathSegment.toLong()
//
// ... do something with event ID
//
//

Java

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 として、または最初の選択項目として指定します。選択は "_id=?" で始まり、最初の selectionArg はイベントの _ID である必要があります。ID のない選択を使用して更新することもできます。イベントを更新する例を次に示します。withAppendedId() アプローチを使用して、予定のタイトルを変更します。

Kotlin

val DEBUG_TAG = "MyActivity"
...
val eventID: Long = 188
...
val values = ContentValues().apply {
    // The new title for the event
    put(CalendarContract.Events.TITLE, "Kickboxing")
}
val updateUri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val rows: Int = contentResolver.update(updateUri, values, null, null)
Log.i(DEBUG_TAG, "Rows updated: $rows")

Java

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 = cr.update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);

予定を削除

イベントを削除するには、URI に追加された ID として _ID を使用するか、標準の選択を使用します。追加された ID を使用する場合は、選択もできません。削除には、アプリケーションとして削除する方法と、同期アダプターとして削除する方法の 2 つがあります。アプリケーションを削除すると、deleted 列が 1 に設定されます。このフラグは、行が削除されたこと、およびこの削除をサーバーに反映する必要があることを同期アダプターに通知します。同期アダプターの削除では、イベントがデータベースから削除され、関連するすべてのデータも削除されます。以下は、_ID を使用してイベントを削除するアプリケーションの例です。

Kotlin

val DEBUG_TAG = "MyActivity"
...
val eventID: Long = 201
...
val deleteUri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val rows: Int = contentResolver.delete(deleteUri, null, null)
Log.i(DEBUG_TAG, "Rows deleted: $rows")

Java

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

参加者テーブル

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

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

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

イベントに対する参加者の関係。次のいずれか 1 つ:

ATTENDEE_TYPE

参加者のタイプ。次のいずれか 1 つ:

ATTENDEE_STATUS

参加者の出欠ステータス。次のいずれか 1 つ:

参加者を追加する

次の例では、1 人の参加者を予定に追加しています。EVENT_ID は必須です。

Kotlin

val eventID: Long = 202
...
val values = ContentValues().apply {
    put(CalendarContract.Attendees.ATTENDEE_NAME, "Trevor")
    put(CalendarContract.Attendees.ATTENDEE_EMAIL, "trevor@example.com")
    put(
        CalendarContract.Attendees.ATTENDEE_RELATIONSHIP,
        CalendarContract.Attendees.RELATIONSHIP_ATTENDEE
    )
    put(CalendarContract.Attendees.ATTENDEE_TYPE, CalendarContract.Attendees.TYPE_OPTIONAL)
    put(
        CalendarContract.Attendees.ATTENDEE_STATUS,
        CalendarContract.Attendees.ATTENDEE_STATUS_INVITED
    )
    put(CalendarContract.Attendees.EVENT_ID, eventID)
}
val uri: Uri = contentResolver.insert(CalendarContract.Attendees.CONTENT_URI, values)

Java

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

リマインダーの表

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

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

定数 説明
EVENT_ID イベントの ID。
MINUTES リマインダーを送信するイベント前の時間(分)。
METHOD

サーバーで設定されたアラーム方法。次のいずれか 1 つ:

リマインダーを追加

この例では、予定にリマインダーを追加します。リマインダーは予定の 15 分前に送信されます。

Kotlin

val eventID: Long = 221
...
val values = ContentValues().apply {
    put(CalendarContract.Reminders.MINUTES, 15)
    put(CalendarContract.Reminders.EVENT_ID, eventID)
    put(CalendarContract.Reminders.METHOD, CalendarContract.Reminders.METHOD_ALERT)
}
val uri: Uri = contentResolver.insert(CalendarContract.Reminders.CONTENT_URI, values)

Java

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

インスタンスのテーブル

CalendarContract.Instances テーブルには、イベントの発生の開始時刻と終了時刻が格納されます。このテーブルの各行は、1 つのイベントの発生を表します。インスタンス テーブルは書き込み可能ではなく、イベントの発生をクエリする方法のみを提供します。

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

定数 説明
BEGIN インスタンスの開始時刻(UTC ミリ秒単位)。
END インスタンスの終了時刻(UTC ミリ秒)。
END_DAY インスタンスのユリウス暦の終了日。カレンダーのタイムゾーンを基準にしています。
END_MINUTE カレンダーのタイムゾーンの午前 0 時から測定されたインスタンスの終了分。
EVENT_ID このインスタンスのイベントの _ID。
START_DAY インスタンスのユリウス暦の開始日(カレンダーのタイムゾーンを基準とする)。
START_MINUTE カレンダーのタイムゾーンを基準として、午前 0 時から測定されたインスタンスの開始分。

instances テーブルをクエリする

Instances テーブルをクエリするには、URI でクエリの範囲時間を指定する必要があります。この例では、CalendarContract.Instances は CalendarContract.EventsColumns インターフェースの実装を通じて TITLE フィールドにアクセスします。つまり、TITLE は、未加工の CalendarContract.Instances テーブルをクエリするのではなく、データベース ビューを介して返されます。

Kotlin

const val DEBUG_TAG: String = "MyActivity"
val INSTANCE_PROJECTION: Array<String> = arrayOf(
        CalendarContract.Instances.EVENT_ID, // 0
        CalendarContract.Instances.BEGIN, // 1
        CalendarContract.Instances.TITLE // 2
)

// The indices for the projection array above.
const val PROJECTION_ID_INDEX: Int = 0
const val PROJECTION_BEGIN_INDEX: Int = 1
const val PROJECTION_TITLE_INDEX: Int = 2

// Specify the date range you want to search for recurring
// event instances
val startMillis: Long = Calendar.getInstance().run {
    set(2011, 9, 23, 8, 0)
    timeInMillis
}
val endMillis: Long = Calendar.getInstance().run {
    set(2011, 10, 24, 8, 0)
    timeInMillis
}

// The ID of the recurring event whose instances you are searching
// for in the Instances table
val selection: String = "${CalendarContract.Instances.EVENT_ID} = ?"
val selectionArgs: Array<String> = arrayOf("207")

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

// Submit the query
val cur: Cursor = contentResolver.query(
        builder.build(),
        INSTANCE_PROJECTION,
        selection,
        selectionArgs, null
)
while (cur.moveToNext()) {
    // Get the field values
    val eventID: Long = cur.getLong(PROJECTION_ID_INDEX)
    val beginVal: Long = cur.getLong(PROJECTION_BEGIN_INDEX)
    val title: String = cur.getString(PROJECTION_TITLE_INDEX)

    // Do something with the values.
    Log.i(DEBUG_TAG, "Event: $title")
    val calendar = Calendar.getInstance().apply {
        timeInMillis = beginVal
    }
    val formatter = SimpleDateFormat("MM/dd/yyyy")
    Log.i(DEBUG_TAG, "Date: ${formatter.format(calendar.time)}")
}

Java

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>

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

VIEW

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

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


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

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

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


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

Events.CONTENT_URI を使用して 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 分まで実行されるイベントをスケジュールします。このコード スニペットについては、次の点に注意してください。

Kotlin

val startMillis: Long = Calendar.getInstance().run {
    set(2012, 0, 19, 7, 30)
    timeInMillis
}
val endMillis: Long = Calendar.getInstance().run {
    set(2012, 0, 19, 8, 30)
    timeInMillis
}
val intent = Intent(Intent.ACTION_INSERT)
        .setData(CalendarContract.Events.CONTENT_URI)
        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, startMillis)
        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endMillis)
        .putExtra(CalendarContract.Events.TITLE, "Yoga")
        .putExtra(CalendarContract.Events.DESCRIPTION, "Group class")
        .putExtra(CalendarContract.Events.EVENT_LOCATION, "The gym")
        .putExtra(CalendarContract.Events.AVAILABILITY, CalendarContract.Events.AVAILABILITY_BUSY)
        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com")
startActivity(intent)

Java

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 インテントを使用すると、権限のないアプリでも予定の編集をカレンダー アプリに引き継ぐことができます。ユーザーがカレンダーで予定の編集を完了すると、元のアプリケーションに戻ります。

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

Kotlin

val eventID: Long = 208
val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val intent = Intent(Intent.ACTION_EDIT)
        .setData(uri)
        .putExtra(CalendarContract.Events.TITLE, "My New Title")
startActivity(intent)

Java

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 つの方法が用意されています。

  • カレンダーを特定の日付で開く。
  • イベントを表示する。

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

Kotlin

val startMillis: Long
...
val builder: Uri.Builder = CalendarContract.CONTENT_URI.buildUpon()
        .appendPath("time")
ContentUris.appendId(builder, startMillis)
val intent = Intent(Intent.ACTION_VIEW)
        .setData(builder.build())
startActivity(intent)

Java

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

次の例は、イベントを開いて表示する方法を示しています。

Kotlin

val eventID: Long = 208
...
val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
startActivity(intent)

Java

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

同期アダプター

アプリケーションと同期アダプタがカレンダー プロバイダにアクセスする方法には、わずかな違いしかありません。

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

同期アダプターで使用する URI を返すために使用できるヘルパー メソッドを次に示します。

Kotlin

fun asSyncAdapter(uri: Uri, account: String, accountType: String): Uri {
    return uri.buildUpon()
            .appendQueryParameter(CalendarContract.CALLER_IS_SYNCADAPTER, "true")
            .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_NAME, account)
            .appendQueryParameter(CalendarContract.Calendars.ACCOUNT_TYPE, accountType).build()
}

Java

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