AndroidアプリからCalendarProviderを使って、Googleカレンダーに登録されているイベントを読み取る方法を紹介します。
User Permissions
まず、カレンダーの読み込みに必要なパーミッションを設定します。
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.xxx">
...
<uses-permission android:name="android.permission.READ_CALENDAR" />
...
</manifest>
- Android 6.0 (Marshmallow) 以降もサポートする際には、追加でRuntime Permissionへの対応が必要です。
カレンダーの一覧取得
カレンダーから取得可能な情報
個々のカレンダーから取得可能な情報は、下記を参照してください。
- CalendarContract.java
- APIリファレンス: CalendarContract.Calendars
以下に、個々のカレンダーから取得可能な情報のうち、主なものを列挙します。
Constant | Type | Description |
---|---|---|
_ID | INTEGER (long) | unique ID |
NAME | TEXT | カレンダーの名前 |
ACCOUNT_NAME | TEXT | 同期の際に使用されるアカウント |
ACCOUNT_TYPE | TEXT | 同期の際に使用されるアカウントの種別 |
CALENDAR_COLOR | INTEGER (color value) | カレンダーの色 (イベントのデフォルトの表示色) |
CALENDAR_DISPLAY_NAME | TEXT | ユーザーに表示されるカレンダーの名前 |
CALENDAR_ACCESS_LEVEL | INTEGER (one of the values below) |
カレンダーに対するアクセスレベル |
CALENDAR_TIME_ZONE | TEXT | カレンダーのタイムゾーン |
VISIBLE | INTEGER (boolean) | カレンダーが表示対象か否か ・ 値 0 の場合、このカレンダーのイベントを表示しない ・値 1 の場合、このカレンダーのイベントを表示する |
SYNC_EVENTS | INTEGER (boolean) | カレンダーを同期し、イベントを端末に保存するか否か ・ 値 0 の場合、同期せず、イベントを端末に保存しない ・値 1 の場合、同期してイベントを端末に保存する |
OWNER_ACCOUNT | TEXT | カレンダーの所有者のアカウント |
なお、CALENDAR_ACCESS_LEVEL は以下のいずれかの値をとります。
Constant | Value | Description |
---|---|---|
CAL_ACCESS_NONE | 0 | カレンダーにアクセスできない |
CAL_ACCESS_FREEBUSY | 100 | カレンダー内の空きの有無を確認可能 |
CAL_ACCESS_READ | 200 | 全てのイベントの詳細が確認可能 |
CAL_ACCESS_RESPOND | 300 | イベントに対してyes/no/maybeで返信可能 |
CAL_ACCESS_OVERRIDE | 400 | ※使用されていない |
CAL_ACCESS_CONTRIBUTOR | 500 | アクセス権以外の全ての編集が可能 |
CAL_ACCESS_EDITOR | 600 | アクセス権以外の全ての編集が可能 |
CAL_ACCESS_OWNER | 700 | カレンダーに対して全ての操作が可能 |
CAL_ACCESS_ROOT | 800 | ドメインの管理者 |
カレンダーの一覧取得
まず、"android.provider.CalendarContract.Calendars"を static import しておきます。
"CalendarContract.Calendars.xxx"のままでは長いため、"Calendars.xxx"でアクセスできるようにします。
import static android.provider.CalendarContract.Calendars;
次に、個々のカレンダーから取得したいプロパティを列挙します。
// プロジェクション配列。
// 取得したいプロパティの一覧を指定する。
private static final String[] CALENDAR_PROJECTION = new String[] {
Calendars._ID,
Calendars.NAME,
Calendars.ACCOUNT_NAME,
Calendars.ACCOUNT_TYPE,
Calendars.CALENDAR_COLOR,
Calendars.CALENDAR_DISPLAY_NAME,
Calendars.CALENDAR_ACCESS_LEVEL,
Calendars.CALENDAR_TIME_ZONE,
Calendars.VISIBLE,
Calendars.SYNC_EVENTS,
Calendars.OWNER_ACCOUNT,
};
// プロジェクション配列のインデックス。
// パフォーマンス向上のために、動的に取得せずに、静的に定義しておく。
private static final int CALENDAR_PROJECTION_IDX_ID = 0;
private static final int CALENDAR_PROJECTION_IDX_NAME = 1;
private static final int CALENDAR_PROJECTION_IDX_ACCOUNT_NAME = 2;
private static final int CALENDAR_PROJECTION_IDX_ACCOUNT_TYPE = 3;
private static final int CALENDAR_PROJECTION_IDX_CALENDAR_COLOR = 4;
private static final int CALENDAR_PROJECTION_IDX_CALENDAR_DISPLAY_NAME = 5;
private static final int CALENDAR_PROJECTION_IDX_CALENDAR_ACCESS_LEVEL = 6;
private static final int CALENDAR_PROJECTION_IDX_CALENDAR_TIME_ZONE = 7;
private static final int CALENDAR_PROJECTION_IDX_VISIBLE = 8;
private static final int CALENDAR_PROJECTION_IDX_SYNC_EVENTS = 9;
private static final int CALENDAR_PROJECTION_IDX_OWNER_ACCOUNT = 10;
最後に、クエリを発行してカーソルを取得し、カーソルから個々のカレンダーの情報を取得します。
以下はCSV形式で、logcatに出力します。
// クエリ条件を設定する
final Uri uri = CalendarContract.Calendars.CONTENT_URI;
final String[] projection = CALENDAR_PROJECTION;
final String selection = null;
final String[] selectionArgs = null;
final String sortOrder = null;
// クエリを発行してカーソルを取得する
final ContentResolver cr = getContentResolver();
final Cursor cur = cr.query(uri, projection, selection, selectionArgs, sortOrder);
// ログ出力 (Header)
final StringBuilder sbHeader = new StringBuilder();
for (final String property : CALENDAR_PROJECTION) {
sbHeader.append(property).append(',');
}
Log.d(TAG, sbHeader.toString());
while (cur.moveToNext()) {
// カーソルから各プロパティを取得する
final long id = cur.getLong(CALENDAR_PROJECTION_IDX_ID);
final String name = cur.getString(CALENDAR_PROJECTION_IDX_NAME);
final String accountName = cur.getString(CALENDAR_PROJECTION_IDX_ACCOUNT_NAME);
final String accountType = cur.getString(CALENDAR_PROJECTION_IDX_ACCOUNT_TYPE);
final int calendarColor = cur.getInt(CALENDAR_PROJECTION_IDX_CALENDAR_COLOR);
final String calendarDisplayName = cur.getString(CALENDAR_PROJECTION_IDX_CALENDAR_DISPLAY_NAME);
final int calendarAccessLevel = cur.getInt(CALENDAR_PROJECTION_IDX_CALENDAR_ACCESS_LEVEL);
final String calendarTimeZone = cur.getString(CALENDAR_PROJECTION_IDX_CALENDAR_TIME_ZONE);
final int visible = cur.getInt(CALENDAR_PROJECTION_IDX_VISIBLE);
final int syncEvents = cur.getInt(CALENDAR_PROJECTION_IDX_SYNC_EVENTS);
final String ownerAccount = cur.getString(CALENDAR_PROJECTION_IDX_OWNER_ACCOUNT);
// ログ出力 (Body)
final StringBuilder sbBody = new StringBuilder();
sbBody.append(id).append(',');
sbBody.append(name).append(',');
sbBody.append(accountName).append(',');
sbBody.append(accountType).append(',');
sbBody.append(String.format("0x%08X", calendarColor)).append(',');
sbBody.append(calendarDisplayName).append(',');
sbBody.append(calendarAccessLevel).append(',');
sbBody.append(calendarTimeZone).append(',');
sbBody.append(visible).append(',');
sbBody.append(syncEvents).append(',');
sbBody.append(ownerAccount).append(',');
Log.d(TAG, sbBody.toString());
}
カレンダーの一覧の取得結果
_id,name,account_name,account_type,calendar_color,calendar_displayName,calendar_access_level,calendar_timezone,visible,sync_events,ownerAccount
1,aaa@gmail.com,aaa@gmail.com,com.google,0xFF9A9CFF,aaa@gmail.com,700,Asia/Tokyo,1,1,aaa@gmail.com
2,共有カレンダー,aaa@gmail.com,com.google,0xFFB99AFF,共有カレンダー,700,Asia/Tokyo,1,1,xxxyyyzzz@group.calendar.google.com
3,Contacts,aaa@gmail.com,com.google,0xFF92E1C0,Contacts,200,Asia/Tokyo,1,1,#contacts@group.v.calendar.google.com
4,日本の祝日,aaa@gmail.com,com.google,0xFF16A765,日本の祝日,200,Asia/Tokyo,1,1,ja.japanese#holiday@group.v.calendar.google.com
5,bbb@gmail.com,bbb@gmail.com,com.google,0xFF9FE1E7,bbb@gmail.com,700,Asia/Tokyo,1,1,bbb@gmail.com
6,Contacts,bbb@gmail.com,com.google,0xFF9A9CFF,Contacts,200,Asia/Tokyo,1,1,#contacts@group.v.calendar.google.com
7,日本の祝日,bbb@gmail.com,com.google,0xFF9A9CFF,日本の祝日,200,Asia/Tokyo,1,1,ja.japanese#holiday@group.v.calendar.google.com
8,docomo,docomo,com.android.nttdocomo,0xFF182C57,com.android.nttdocomo,700,Asia/Tokyo,0,1,docomo
イベントの一覧取得
特定のカレンダーに登録されているイベントの一覧を取得します。
個々のイベントから取得可能な情報は、下記を参照してください。
- CalendarContract.java
- APIリファレンス: CalendarContract.Events
以下に、個々のイベントから取得可能な情報のうち、主なものを列挙します。
Constant | Type | Description |
---|---|---|
CALENDAR_ID | NTEGER (long) | イベントが属するカレンダーの _ID |
TITLE | TEXT | イベントのタイトル |
DESCRIPTION | TEXT | イベントの説明 |
EVENT_LOCATION | TEXT | イベントの開催場所 |
EVENT_COLOR | INTEGER (color value) | イベントの表示色 |
DISPLAY_COLOR | INTEGER (color value) | イベントの実際の表示色 EVENT_COLORが未設定の場合には、CALENDAR_COLORが使用される |
DTSTART | INTEGER (long; millis since epoch) | イベントの開始日時 UTCでの1970/01/01 00:00:00からのミリ秒単位での経過時刻で表現される |
DTEND | INTEGER (long; millis since epoch) | イベントの終了日時 UTCでの1970/01/01 00:00:00からのミリ秒単位での経過時刻で表現される |
DURATION | TEXT (duration in RFC5545 format) | イベントの期間 RFC5545(旧版RFC2445)の形式で表現される 例)"PT1H"⇒1時間、 "P2W"⇒2週間 |
EVENT_TIMEZONE | TEXT | イベントのタイムゾーン |
EVENT_END_TIMEZONE | TEXT | イベントの終了日時のタイムゾーン |
ALL_DAY | INTEGER (boolean) | 終日のイベントか否か |
RRULE | TEXT | イベントの繰り返しルール RFC5545(旧版RFC2445)の形式で表現される 例)"FREQ=WEEKLY;COUNT=10;WKST=SU" |
RDATE | TEXT | イベントの繰り返し発生日 RRULE と組み合わせて、繰り返しのイベント全体を定義するのに使用される |
GUESTS_CAN_MODIFY | INTEGER (boolean) | ゲストがイベントを変更できるか否か |
GUESTS_CAN_INVITE_OTHERS | INTEGER (boolean) | ゲストが他のゲストを招待できるか否か |
GUESTS_CAN_SEE_GUESTS | INTEGER (boolean) | ゲストが他のゲストのリストを参照できるか否か |
ORGANIZER | STRING | イベントの主催者(所有者)のメールアドレス |
カレンダー内のイベントの一覧取得
まず、"android.provider.CalendarContract.Events"を static import しておきます。
"CalendarContract.Events.xxx"のままでは長いため、"Events.xxx"でアクセスできるようにします。
import static android.provider.CalendarContract.Events;
次に、個々のイベントから取得したいプロパティを列挙します。
// プロジェクション配列。
// 取得したいプロパティの一覧を指定する。
public static final String[] EVENT_PROJECTION = new String[] {
Events.CALENDAR_ID,
Events.TITLE,
Events.DESCRIPTION,
Events.EVENT_LOCATION,
Events.EVENT_COLOR,
Events.DISPLAY_COLOR,
Events.DTSTART,
Events.DTEND,
Events.DURATION,
Events.EVENT_TIMEZONE,
Events.EVENT_END_TIMEZONE,
Events.ALL_DAY,
Events.RRULE,
Events.RDATE,
Events.GUESTS_CAN_MODIFY,
Events.GUESTS_CAN_INVITE_OTHERS,
Events.GUESTS_CAN_SEE_GUESTS,
Events.ORGANIZER,
};
// プロジェクション配列のインデックス。
// パフォーマンス向上のために、動的に取得せずに、静的に定義しておく。
private static final int EVENT_PROJECTION_IDX_ID = 0;
private static final int EVENT_PROJECTION_IDX_CALENDAR_ID = 1;
private static final int EVENT_PROJECTION_IDX_TITLE = 2;
private static final int EVENT_PROJECTION_IDX_DESCRIPTION = 3;
private static final int EVENT_PROJECTION_IDX_EVENT_LOCATION = 4;
private static final int EVENT_PROJECTION_IDX_EVENT_COLOR = 5;
private static final int EVENT_PROJECTION_IDX_DISPLAY_COLOR = 6;
private static final int EVENT_PROJECTION_IDX_DTSTART = 7;
private static final int EVENT_PROJECTION_IDX_DTEND = 8;
private static final int EVENT_PROJECTION_IDX_DURATION = 9;
private static final int EVENT_PROJECTION_IDX_EVENT_TIMEZONE = 10;
private static final int EVENT_PROJECTION_IDX_EVENT_END_TIMEZONE = 11;
private static final int EVENT_PROJECTION_IDX_ALL_DAY = 12;
private static final int EVENT_PROJECTION_IDX_RRULE = 13;
private static final int EVENT_PROJECTION_IDX_RDATE = 14;
private static final int EVENT_PROJECTION_IDX_GUESTS_CAN_MODIFY = 15;
private static final int EVENT_PROJECTION_IDX_GUESTS_CAN_INVITE_OTHERS = 16;
private static final int EVENT_PROJECTION_IDX_GUESTS_CAN_SEE_GUESTS = 17;
private static final int EVENT_PROJECTION_IDX_ORGANIZER = 18;
ここで、UNIX time (1970/01/01 00:00:00からの経過時刻[msec])を文字列化するためのメソッドを用意しておきます。
private static String getDateTimeText(
final Context context,
final String timeZone,
final long dateTimeInMillis
) {
final Calendar calendar = GregorianCalendar.getInstance(TimeZone.getTimeZone(timeZone));
calendar.setTimeInMillis(dateTimeInMillis);
return DateUtils.formatDateRange(context, new java.util.Formatter(), calendar.getTimeInMillis(), calendar.getTimeInMillis(),
DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_SHOW_YEAR, timeZone).toString();
}
最後に、検索対象のカレンダーの _ID を指定し、クエリを発行してカーソルを取得し、カーソルから個々のイベントの情報を取得します。
以下はCSV形式で、logcatに出力します。
// 検索対象のカレンダーの _ID
final long targetCalendarId = 1;
// クエリ条件を設定する
final Uri uri = CalendarContract.Events.CONTENT_URI;
final String[] projection = CALENDAR_PROJECTION;
final String selection = "(" + CalendarContract.Events.CALENDAR_ID + " = ?)";
final String[] selectionArgs = new String[] {String.valueOf(targetCalendarId)};
final String sortOrder = null;
// クエリを発行してカーソルを取得する
final ContentResolver cr = getContentResolver();
final Cursor cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);
// ログ出力 (Header)
final StringBuilder sbEvents = new StringBuilder();
for (final String property : EVENT_PROJECTION) {
sbEvents.append(property).append(',');
}
Log.d(TAG, sbEvents.toString());
while (cur.moveToNext()) {
// カーソルから各プロパティを取得する
final long id = cur.getLong(EVENT_PROJECTION_IDX_ID);
final long calendarId = cur.getLong(EVENT_PROJECTION_IDX_CALENDAR_ID);
final String title = cur.getString(EVENT_PROJECTION_IDX_TITLE);
final String description = cur.getString(EVENT_PROJECTION_IDX_DESCRIPTION);
final String eventLocation = cur.getString(EVENT_PROJECTION_IDX_EVENT_LOCATION);
final int eventColdr = cur.getInt(EVENT_PROJECTION_IDX_EVENT_COLOR);
final int displayColor = cur.getInt(EVENT_PROJECTION_IDX_DISPLAY_COLOR);
final long dtStart = cur.getLong(EVENT_PROJECTION_IDX_DTSTART);
final long dtEnd = cur.getLong(EVENT_PROJECTION_IDX_DTEND);
final String duration = cur.getString(EVENT_PROJECTION_IDX_DURATION);
final String eventTimeZone = cur.getString(EVENT_PROJECTION_IDX_EVENT_TIMEZONE);
final String eventEndTimeZone = cur.getString(EVENT_PROJECTION_IDX_EVENT_END_TIMEZONE);
final int allDay = cur.getInt(EVENT_PROJECTION_IDX_ALL_DAY);
final String rRule = cur.getString(EVENT_PROJECTION_IDX_RRULE);
final String rDate = cur.getString(EVENT_PROJECTION_IDX_RDATE);
final int guestsCanModify = cur.getInt(EVENT_PROJECTION_IDX_GUESTS_CAN_MODIFY);
final int guestsCanInviteOthers = cur.getInt(EVENT_PROJECTION_IDX_GUESTS_CAN_INVITE_OTHERS);
final int guestCanSeeGuests = cur.getInt(EVENT_PROJECTION_IDX_GUESTS_CAN_SEE_GUESTS);
final String organizer = cur.getString(EVENT_PROJECTION_IDX_ORGANIZER);
// ログ出力 (Body)
final StringBuilder sbBody = new StringBuilder();
sbBody.append(id).append(',');
sbBody.append(name).append(',');
sbBody.append(accountName).append(',');
sbBody.append(accountType).append(',');
sbBody.append(String.format("0x%08X", calendarColor)).append(',');
sbBody.append(calendarDisplayName).append(',');
sbBody.append(calendarAccessLevel).append(',');
sbBody.append(calendarTimeZone).append(',');
sbBody.append(visible).append(',');
sbBody.append(syncEvents).append(',');
sbBody.append(ownerAccount).append(',');
Log.d(TAG, sbBody.toString());
}
イベントの一覧の取得結果
_id,calendar_id,title,description,eventLocation,eventColor,displayColor,dtstart,dtend,duration,eventTimezone,eventEndTimezone,allDay,rrule,rdate,guestsCanModify,guestsCanInviteOthers,guestsCanSeeGuests,organizer,
1,1,Google Developers Summit : Android,,コクヨホール,0x00000000,0xFF9A9CFF,2016年4月25日 13:00,2016年4月25日 17:30,null,Asia/Tokyo,null,0,null,null,0,1,1,aaa@gmail.com,
2,1,日本Androidの会 2016年9月定例会,「クロスプラットフォーム開発最前線」,日本マイクロソフト株式会社,0xFF46D6DB,0xFF46D6DB,2016年9月7日 19:00,2016年9月7日 21:40,null,Asia/Tokyo,null,0,null,null,0,1,1,aaa@gmail.com,
3,1,shibuya.apk #10,,株式会社サイバーエージェント,0xFF46D6DB,0xFF46D6DB,2016年9月16日 19:00,2016年9月16日 21:00,null,Asia/Tokyo,null,0,null,null,0,1,1,aaa@gmail.com,
4,1,daily meeting,,,0x00000000,0xFF9A9CFF,2016年10月11日 10:00,1970年1月1日 9:00,P3600S,Asia/Tokyo,null,0,FREQ=WEEKLY;WKST=SU;BYDAY=MO,TU,WE,TH,FR,null,0,1,1,aaa@gmail.com,