Android から CalendarProviderを使ってGoogleカレンダーにイベントを追加する

AndroidアプリからCalendarProviderを使って、Googleカレンダーに新規にイベントを追加する方法を紹介します。

# User Permissions

カレンダーの読み書きに必要なパーミッションを設定しておきます。

```xml:AndroidManifets.xml
<?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" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    ...
</manifest>
```

* Android 6.0 (Marshmallow) 以降もサポートする際には、追加でRuntime Permissionへの対応が必要です。

# カレンダーの一覧取得

イベントの追加対象であるカレンダーを見つけるために、CalendarProviderを使ってカレンダーの一覧を取得します。
詳細は、[「CalendarProviderを使ってGoogleカレンダーにアクセスする」](http://qiita.com/couzie/items/ce01ce4e0ae210235010)をご確認ください。

まず、"android.provider.CalendarContract.Calendars"を static import しておきます。

```java
import static android.provider.CalendarContract.Calendars;
```

次に、個々のカレンダーについて取得したいプロパティを列挙します。

```java
    // プロジェクション配列。
    // 取得したいプロパティの一覧を指定する。
    private static final String[] CALENDAR_PROJECTION = new String[] {
            Calendars._ID,
            Calendars.NAME,
            Calendars.ACCOUNT_NAME,
            Calendars.ACCOUNT_TYPE,
            Calendars.CALENDAR_COLOR,
            Calendars.CALENDAR_COLOR_KEY,
            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_COLOR_KEY = 5;
    private static final int CALENDAR_PROJECTION_IDX_CALENDAR_DISPLAY_NAME = 6;
    private static final int CALENDAR_PROJECTION_IDX_CALENDAR_ACCESS_LEVEL = 7;
    private static final int CALENDAR_PROJECTION_IDX_CALENDAR_TIME_ZONE = 8;
    private static final int CALENDAR_PROJECTION_IDX_VISIBLE = 9;
    private static final int CALENDAR_PROJECTION_IDX_SYNC_EVENTS = 10;
    private static final int CALENDAR_PROJECTION_IDX_OWNER_ACCOUNT = 11;
```

最後に、クエリを発行してカーソルを取得し、カーソルから個々のカレンダーの情報を取得します。
（事前に絞り込むための条件が明確な場合には、`selection/selectionArgs`に値を指定してください）

```java
        // クエリ条件を設定する
        final Uri uri = 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);

        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 calendarColorkey = cur.getString(CALENDAR_PROJECTION_IDX_CALENDAR_COLOR_KEY);
            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);
        }
```

これにより、イベント追加先のGoogleカレンダーを選択するための情報が得られました。
また、イベント追加先のGoogleカレンダーに関して、以下の情報も得られました。
  - カレンダーの `_ID`（例: 1）
  - `ACCOUNT_NAME`（例: "aaa@gmail.com"）
  - `ACCOUNT_TYPE`（例: "com.google"）


# 色情報の一覧取得

カレンダーやイベントに設定できる色情報に関しては、カレンダーやイベントのテーブルとは別のテーブルでに管理されています。

CalendarProvider を使うことで、特定のGoogleアカウントにおいて、イベントに使用可能な色情報の一覧を取得することができます。

まず、"android.provider.CalendarContract.Colors" を static import しておきます。

```java
import static android.provider.CalendarContract.Colors;
```

次に、個々の色情報について取得したいプロパティを列挙します。

```java
    // プロジェクション配列
    private static final String[] COLOR_PROJECTION = new String[] {
            Colors._ID,
            Colors.COLOR,
            Colors.COLOR_KEY,
            Colors.COLOR_TYPE,
            Colors.ACCOUNT_NAME,
            Colors.ACCOUNT_TYPE,
    };
    
    // プロジェクション配列のインデックス
    private static final int COLOR_PROJECTION_IDX_ID = 0;
    private static final int COLOR_PROJECTION_IDX_COLOR = 1;
    private static final int COLOR_PROJECTION_IDX_COLOR_KEY = 2;
    private static final int COLOR_PROJECTION_IDX_COLOR_TYPE = 3;
    private static final int COLOR_PROJECTION_IDX_ACCOUNT_NAME = 4;
    private static final int COLOR_PROJECTION_IDX_ACCOUNT_TYPE = 5;
```

最後に、クエリを発行してカーソルを取得し、カーソルから個々の色情報を取得します。

```java
    private void queryColors(
            final String accountName,    // 例）"aaa@google.com"
            final String accountType       // 例）"com.google"
    ) {
        // クエリ条件を設定する
        final Uri uri = Colors.CONTENT_URI;
        final String[] projection = COLOR_PROJECTION;
        // 特定のアカウントで、イベントの色情報を持つレコードのみを対象とする
        final String selection =
                "((" + Colors.ACCOUNT_NAME + " = ?)"
                + "AND (" + Colors.ACCOUNT_TYPE + " = ?)"
                + "AND (" + Colors.COLOR_TYPE + " = ?))";
        final String[] selectionArgs = new String[] {
                accountName,
                accountType,
                String.valueOf(Colors.TYPE_EVENT),
        };
        final String sortOrder = null;

        // クエリを発行してカーソルを取得する
        final ContentResolver cr = getContentResolver();
        final Cursor cur = cr.query(uri, projection, selection, selectionArgs, sortOrder);

        while (cur.moveToNext()) {
            // カーソルから各プロパティを取得する
            final long id = cur.getLong(COLOR_PROJECTION_IDX_ID);
            final int color = cur.getInt(COLOR_PROJECTION_IDX_COLOR);
            final String colorKey = cur.getString(COLOR_PROJECTION_IDX_COLOR_KEY);
            final String colorType = cur.getString(COLOR_PROJECTION_IDX_COLOR_TYPE);
        }
    }
```

これにより、以下のような、イベントに使用可能な色情報のテーブルが作成できました。
イベントを追加する際に、以下の **color_index** の値でイベントの色を指定します。


| _id | color_index | color | Googleカレンダーでの表記 |
|--:|--:|:--|:--|
| 33 | 11 | 0xFFDC2127 | Tomato / トマト |
| 28 | 6 | 0xFFFFB878 | Tangerine / ミカン |
| 30 | 5 | 0xFFFBD75B | Banana / バナナ |
| 27 | 10 | 0xFF51B749 | Basil / バジル |
| 35 | 2 | 0xFF7AE7BF | Sage / セージ |
| 31 | 7 | 0xFF46D6DB | Peacock / ピーコック |
| 32 | 9 | 0xFF5484ED | Blueberry / ブルーベリー |
| 25 | 1 | 0xFFA4BDFC | Lavender / ラベンダー |
| 34 | 3 | 0xFFDBADFF | Grape / ブドウ |
| 29 | 4 | 0xFFFF887C | Flamingo / フラミンゴ |
| 26 | 8 | 0xFFE1E1E1 | Graphite / グラファイト |

この配色は、Googleカレンダーでイベントの色を変更する際に表示されるリストと同じです。

![device-2016-10-12-152024.png](https://qiita-image-store.s3.amazonaws.com/0/93531/1e0041f2-bf55-71cc-ea5c-c1c0f1d20208.png)

なお、いくつかのGoogleアカウントで確認してみましたが、現時点ではいずれも上記の配色で固定されていました。
（変更する方法は、あるのでしょうか？）


# イベントの追加



まず、"android.provider.CalendarContract.Events" を static import しておきます。

```java
import static android.provider.CalendarContract.Events;
```

あとは、`ContentValues`にイベント追加先のGoogleカレンダーの`_ID`とイベントの各種プロパティを設定し、`ContentResolver`の`insert()`を呼び出すと、Googleカレンダーにイベントが追加されます。

```java
    private long addEvent(
            final long calendarId,
            final String title,
            final String description,
            final String colorKey,
            final long startMillis,
            final long endMillis
    ) {
        final ContentResolver cr = getContentResolver();

        final ContentValues values = new ContentValues();
        values.put(Events.CALENDAR_ID, calendarId);
        values.put(Events.TITLE, title);
        values.put(Events.DESCRIPTION, description);
        values.put(Events.EVENT_COLOR_KEY, colorKey);
        values.put(Events.EVENT_TIMEZONE, TimeZone.getDefault().getID());
        values.put(Events.DTSTART, startMillis);
        values.put(Events.DTEND, endMillis);

        final Uri uri = cr.insert(Events.CONTENT_URI, values);

        final long eventId = Long.parseLong(uri.getLastPathSegment());
        return eventId;
    }
```

なお、呼び出し側は以下のようになります。

```java
        final long calendarId = 1;
        final String eventColorKey = "4";

        final long now = new Date().getTime();
        final long anHourLater = now + 60 * 60 * 1000;
        this.addEvent(calendarId, "イベントのタイトル", "イベントの説明", eventColorKey, now, anHourLater);
```

以下の通り、イベントが追加されました。

![device-2016-10-12-200306.png](https://qiita-image-store.s3.amazonaws.com/0/93531/1a34d274-9762-ce52-8e10-9ac5853420e9.png)


# 参考情報

- [Calendar Provider | Android Developers](https://developer.android.com/guide/topics/providers/calendar-provider.html)

- [Calendar Provider | Android Developers (日本語)](https://developer.android.com/guide/topics/providers/calendar-provider.html?hl=ja)