カレンダー プロバイダは、ユーザーのカレンダーの予定のリポジトリです。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. カレンダー プロバイダのデータモデル。
1 人のユーザーが複数のカレンダーを持つことができ、別々のカレンダーを異なる種類のアカウント(Google カレンダー、Exchange など)に関連付けることができます。
CalendarContract
は、カレンダーと予定関連の情報のデータモデルを定義します。このデータは、以下に示すいくつかのテーブルに保存されます。
テーブル(クラス) | 説明 |
---|---|
このテーブルにはカレンダー固有の情報が格納されます。この表の各行には、1 つのカレンダーの詳細(名前、色、同期情報など)が含まれています。 | |
CalendarContract.Events |
このテーブルにはイベント固有の情報が保持されます。このテーブルの各行には、1 つのイベントの情報(イベントのタイトル、場所、開始時間、終了時間など)が含まれています。イベントは 1 回だけ発生する場合もありますが、複数回繰り返される場合もあります。参加者、リマインダー、拡張プロパティは別々のテーブルに保存されます。各イベントには、イベント テーブルの _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
をご覧ください。
// 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
// 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
オブジェクトを返します。このオブジェクトを使用して、データベース クエリから返された結果セットを走査できます。コンテンツ プロバイダでのクエリの使用について詳しくは、コンテンツ プロバイダをご覧ください。
// 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)
// 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);
次のセクションでは、カーソルを使用して結果セットをステップ スルーします。サンプルの冒頭で設定した定数を使用して、各フィールドの値を返します。
// 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...
}
// 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...
...
}
カレンダーを変更する
カレンダーを更新するには、URI(withAppendedId()
)に追加された ID または最初の選択項目として、カレンダーの _ID
を指定します。選択範囲は "_id=?"
で開始し、最初の selectionArg
はカレンダーの _ID
にする必要があります。URI 内の ID をエンコードして更新することもできます。この例では、カレンダーの表示名を withAppendedId()
手法で変更しています。
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")
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
権限を含める必要があります。
次のイベント列は、アプリケーションと同期アダプターの両方で書き込み可能です。サポートされているフィールドの一覧については、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" その他の例については、こちらをご覧ください。 |
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
インテントを使用してイベントを挿入する場合は適用されません。その場合は、RRULE
をDTSTART
およびDTEND
と組み合わせて使用すると、カレンダー アプリによって自動的に期間に変換されます。
次に、イベントの挿入例を示します。わかりやすくするために、これは UI スレッドで実行されます。実際には、挿入と更新を非同期スレッドで実行して、アクションをバックグラウンド スレッドに移動する必要があります。詳細については、AsyncQueryHandler
をご覧ください。
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
//
//
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 に追加の ID(withAppendedId()
)として、または最初の選択アイテムとして指定します。選択は "_id=?"
で始まり、最初の selectionArg
はイベントの _ID
である必要があります。ID のない選択を使用して更新することもできます。次に、イベントの更新の例を示します。withAppendedId()
アプローチを使用してイベントのタイトルを変更します。
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")
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);
予定を削除
イベントは、_ID
(URI に ID を追加)または標準の選択方法で削除できます。追加 ID を使用した場合は、選択も行えません。削除には、アプリケーションと同期アダプターの 2 つのバージョンがあります。アプリケーションの削除では、deleted 列は 1 に設定されます。このフラグは、行が削除されたことと、この削除をサーバーに伝播する必要があることを同期アダプターに伝えます。同期アダプタを削除すると、イベントは、関連するすべてのデータとともにデータベースから削除されます。次に、アプリケーションが _ID
を使用してイベントを削除する例を示します。
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")
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 |
参加者とイベントの関係。次のいずれか: |
ATTENDEE_TYPE |
参加者のタイプ。次のいずれか: |
ATTENDEE_STATUS |
参加者の出席状況。次のいずれか: |
参加者を追加する
次の例では、予定に参加者を 1 人追加しています。EVENT_ID
は必須です。
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)
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
をご覧ください。
リマインダーを追加
この例では、予定にリマインダーを追加します。リマインダーはイベントの 15 分前に呼び出されます。
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)
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 つのイベントの発生を表します。instances テーブルは書き込み不可能で、イベントの発生をクエリする方法のみです。
次の表に、インスタンスでクエリを実行できるフィールドの一部を示します。タイムゾーンは KEY_TIMEZONE_TYPE
と KEY_TIMEZONE_INSTANCES
で定義されます。
定数 | 説明 |
---|---|
BEGIN |
インスタンスの開始時刻(UTC ミリ秒単位)。 |
END |
インスタンスの終了時間(UTC ミリ秒単位)。 |
END_DAY |
インスタンスのジュリアン形式の終了日。カレンダーのタイムゾーンを基準としています。 |
END_MINUTE |
カレンダーのタイムゾーンの午前 0 時を基準としたインスタンスの終了分。 |
EVENT_ID |
このインスタンスのイベントの _ID 。 |
START_DAY |
インスタンスのジュリアン標準日(カレンダーのタイムゾーンを基準とする)。 |
START_MINUTE |
カレンダーのタイムゾーンを基準とする、午前 0 時を基準としたインスタンスの開始分。 |
インスタンス テーブルに対してクエリを実行する
インスタンス テーブルをクエリするには、URI でクエリの範囲時間を指定する必要があります。この例では、CalendarContract.Instances
は、CalendarContract.EventsColumns
インターフェースの実装を通じて TITLE
フィールドへのアクセスを取得します。つまり、TITLE
は、未加工の CalendarContract.Instances
テーブルをクエリするのではなく、データベース ビューを介して返されます。
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)}")
}
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 |
CalendarContract.CONTENT_URI を使用して参照することもできます。このインテントの使用例については、インテントを使用してカレンダー データを表示するをご覧ください。 |
<ms_since_epoch> で指定された時刻にカレンダーを開きます。 |
なし。 |
Events.CONTENT_URI を使用して参照することもできます。このインテントの使用例については、インテントを使用してカレンダー データを表示するをご覧ください。 |
<event_id> で指定されたイベントを表示します。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_END_TIME |
|
EDIT |
Events.CONTENT_URI を使用して参照することもできます。このインテントの使用例については、インテントを使用したイベントの編集をご覧ください。 |
<event_id> で指定されたイベントを編集します。 |
CalendarContract.EXTRA_EVENT_BEGIN_TIME CalendarContract.EXTRA_EVENT_END_TIME |
EDIT INSERT |
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 分までのイベントをスケジュール設定します。このコード スニペットについては、次の点に注意してください。
- URI として
Events.CONTENT_URI
を指定しています。 - 追加フィールド
CalendarContract.EXTRA_EVENT_BEGIN_TIME
とCalendarContract.EXTRA_EVENT_END_TIME
を使用して、フォームにイベントの時刻が事前入力されます。これらの値は、エポックからの UTC ミリ秒単位であることが必要です。 Intent.EXTRA_EMAIL
追加フィールドを使用して、メールアドレスで指定された招待者のカンマ区切りリストを指定します。
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)
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
インテントを使用すると、権限のないアプリからカレンダー アプリに予定の編集を引き継ぐことができます。ユーザーがカレンダーで予定の編集を終了すると、元のアプリケーションに戻ります。
以下は、指定された予定の新しいタイトルを設定し、ユーザーがカレンダーで予定を編集できるようにするインテントの例です。
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)
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 種類の方法が用意されています。
- 特定の日付のカレンダーを開く。
- イベントを表示する。
次の例は、特定の日付のカレンダーを開く方法を示しています。
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)
// 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);
イベントを開いて閲覧する方法の例を以下に示します。
val eventID: Long = 208
...
val uri: Uri = ContentUris.withAppendedId(CalendarContract.Events.CONTENT_URI, eventID)
val intent = Intent(Intent.ACTION_VIEW).setData(uri)
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_SYNCADAPTER
をtrue
に設定して、それが同期アダプターであることを示す必要があります。 - 同期アダプターは、URI 内のクエリ パラメータとして
ACCOUNT_NAME
とACCOUNT_TYPE
を指定する必要があります。 - 同期アダプターは、アプリケーションやウィジェットよりも多くの列への書き込みアクセス権を持ちます。たとえば、アプリケーションで変更できるカレンダーの特性は、名前、表示名、公開設定、カレンダーが同期されているかどうかなど、ごくわずかです。これに対し、同期アダプターはこれらの列だけでなく、カレンダーの色、タイムゾーン、アクセスレベル、場所など、他の多くの列にもアクセスできます。
ただし、同期アダプターは、指定された
ACCOUNT_NAME
とACCOUNT_TYPE
に限定されます。
同期アダプターで使用する URI を返すためのヘルパー メソッドを次に示します。
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()
}
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();
}