Windows環境を前提として、Android から"Google Calendar API"を使って、Googleカレンダーに新規カレンダーを追加する方法を紹介します。
多くの部分が、以下の内容を元に加筆・修正しています。
環境の準備
まず、以下を準備してください。
- JDK 1.8.0 以降
- Android Studio 1.2 以降
- Android SDK Packages
- Android 6.0 (API 23) 以降
- 最新版のGoogle Repository
- 最新版のGoogle Play Services
- 最新版のAndroid Support Library
- インターネットに接続可能なAndroidデバイス
- Googleカレンダーを使用可能なGoogleアカウント
以降は、下記の環境にて確認を行っています。
Windows 8.1 (64bit)
JDK 1.8.0 (Java SE Development Kit 8u101)
Android Studio 2.2
Android 7.0 (API 24) Packages
Google Repository rev.36
Google Play Services rev.33
Android Support Library rev.24
Xperia Z3 Compact (SO-02G) / Android 6.0.1 (Marshmallow)
また、環境変数 Path に以下を追加しておきます。
- JDK 1.8.0 の bin ディレクトリ
- 例) C:\Program Files\Java\jdk1.8.0_101\bin
なお、%USERPROFILE%で表わされる「C:\Users\ユーザー名」ディレクトリの直下に「.android」ディレクトリが存在するものとします。
Step 1: SHA1のfingerprintを取得する
JDKに含まれる"Keytool"を使用して、"Google Calendar API"を使用するためにSHA1のfingerprintを取得します。
("KeyTool"の詳しい使用方法は"アプリケーションへの署名 | Android Developers"などを参照してください)
- コマンドプロンプトを起動してください
- 以下を入力してください
> keytool -exportcert -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore -list -v
- パスワードを聞かれたら android と入力してください
コマンドプロンプトに、以下のように出力されます。
> keytool -exportcert -alias androiddebugkey -keystore %USERPROFILE%\.android\debug.keystore -list -v
キーストアのパスワードを入力してください: android
別名: androiddebugkey
作成日: 2014/12/04
エントリ・タイプ: PrivateKeyEntry
証明書チェーンの長さ: 1
証明書[1]:
所有者: C=US, O=Android, CN=Android Debug
発行者: C=US, O=Android, CN=Android Debug
シリアル番号: 503bd581
有効期間の開始日: Mon Aug 27 13:16:01 PDT 2012終了日: Wed Aug 20 13:16:01 PDT 2042
証明書のフィンガプリント:
MD5: 1B:2B:2D:37:E1:CE:06:8B:A0:F0:73:05:3C:A3:63:DD
SHA1: D8:AA:43:97:59:EE:C5:95:26:6A:07:EE:1C:37:8E:F4:F0:C8:05:C8
SHA256: F3:6F:98:51:9A:DF:C3:15:4E:48:4B:0F:91:E3:3C:6A:A0:97:DC:0A:3F:B2:D2:E1:FE:23:57:F5:EB:AC:13:30
署名アルゴリズム名: SHA1withRSA
バージョン: 3
上記の"D8:AA:43:97:59:EE:C5:95:26:6A:07:EE:1C:37:8E:F4:F0:C8:05:C8"がSHA1のfingerprintになります。
production用のアプリの場合には、debug.keystoreの代わりに、production用のkeystoreを指定する必要があります。
Step 2: Google Calendar API を有効にする
- 以下のlinkをクリックして、Google Developers Console を開いてください
- 「Google API コンソールで Google Calendar API を有効にするアプリケーションの登録」画面が表示されるので、[同意して続行]/[続行]を押してください
- 「API が有効化されました」画面が表示されるので、**[認証情報に進む]**を押してください
- 「プロジェクトへの認証情報の追加」画面が表示されるで、**[キャンセル]**を押してください
- 画面上部の**[OAuth同意画面]**タブを選択してください
- 「OAuth同意画面」でメールアドレスとサービス名を入力し、**[保存]**を押してください
- 画面上部の**[認証情報]**タブを選択してください
- [認証情報を作成]を押し、**[OAuthクライアントID]**を選択してください
- 「クライアントIDの作成」画面が表示されるので、以下を入力し、**[作成]**を押してください
Step 3: Android のプロジェクトを新規に作成する
- Android Studioを起動し、**"Start a new Android Studio project"**を選択してください
- 既に別のprojectを開いている場合にはメニューバーから [File]->[New]->[New Project...] を選択してください
- [New Project]画面が表示されたら、以下を入力して[Next]を押してください
- Application name: CalendarQuickstart
- Company Domain: example.com
- [Target Android Devices]画面が表示されたら、以下を設定して[Next]を押してください
- **"Phone and Tablet"**のみにチェックを入れてください
- "Phone and Tablet"の"Minimum SDK"を**[API 15: Android 4.0.3 (IceCreamSandwich)]**に設定してください
- [Add an Activity to Mobile]画面が表示されたら**[Add No Activity]**を選択して[Finish]を押してください
Step 4: プロジェクトを準備する
a) テスト用のプロジェクトを削除する
- Android Studioの左側に表示されている"1: Project"ウィンドウから[app]->[java]を開いてください
- 今回は不要のため、以下のフォルダは削除してください
- com.example.calendarquickstart (androidTest)
- com.example.calendarquickstart (test)
b) build.gradle (Module: app) を編集する
- "1: Project"ウィンドウから[Gradle Scripts]->**[build.gradle (Module: app)]**を開いてください
- "build.gradle (Module: app)"を以下の内容で差し換えてください
apply plugin: 'com.android.application'
android {
compileSdkVersion 24
buildToolsVersion "24.0.3"
defaultConfig {
applicationId "com.example.calendarquickstart"
minSdkVersion 15
targetSdkVersion 24
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:24.2.1'
compile 'com.google.android.gms:play-services-auth:9.6.1'
compile 'pub.devrel:easypermissions:0.1.5'
compile('com.google.api-client:google-api-client-android:1.22.0') {
exclude group: 'org.apache.httpcomponents'
}
compile('com.google.apis:google-api-services-calendar:v3-rev214-1.22.0') {
exclude group: 'org.apache.httpcomponents'
}
}
- 編集後にメニューバーから [Tools]->[Android]->**[Sync Project with Gradle Files]**を選択してください
- build.gradleの内容を元に必要なファイルがdownloadされ、makeが実行されます
c) AndroidManifest.xml を編集する
- "1: Project"ウィンドウから[app]->[manifests]->**[AndroidManifest.xml]**を開いてください
- "AndroidManifest.xml"を以下の内容で差し換えてください
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.calendarquickstart">
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="Google Calendar API Android Quickstart"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:label="Google Calendar API Android Quickstart" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
</application>
</manifest>
Step 5: アプリをセットアップする
- "1: Project"ウィンドウから[app]->[java]->[com.example.calendarquickstart]を選択する
- 右クリックメニューから[New]->[Java Class]を選択する
- [Create New Class]ダイアログが表示されたら、以下を入力して[OK]を押す
- Name: MainActivity
- "MainActivity.java"を以下の内容で差し換えてください
package com.example.calendarquickstart;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.extensions.android.gms.auth.GooglePlayServicesAvailabilityIOException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.json.JsonFactory;
import com.google.api.client.json.jackson2.JacksonFactory;
import com.google.api.client.util.ExponentialBackOff;
import com.google.api.services.calendar.CalendarScopes;
import com.google.api.client.util.DateTime;
import com.google.api.services.calendar.model.*;
import android.Manifest;
import android.accounts.AccountManager;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.text.TextUtils;
import android.text.method.ScrollingMovementMethod;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import pub.devrel.easypermissions.AfterPermissionGranted;
import pub.devrel.easypermissions.EasyPermissions;
public class MainActivity extends Activity implements EasyPermissions.PermissionCallbacks {
GoogleAccountCredential mCredential;
private TextView mOutputText;
private Button mCallApiButton;
ProgressDialog mProgress;
static final int REQUEST_ACCOUNT_PICKER = 1000;
static final int REQUEST_AUTHORIZATION = 1001;
static final int REQUEST_GOOGLE_PLAY_SERVICES = 1002;
static final int REQUEST_PERMISSION_GET_ACCOUNTS = 1003;
private static final String BUTTON_TEXT = "Call Google Calendar API";
private static final String PREF_ACCOUNT_NAME = "accountName";
private static final String[] SCOPES = {CalendarScopes.CALENDAR};
/**
* Activityを作成する。
*
* @param savedInstanceState 以前に保存されたインスタンスのデータ
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Activity のレイアウトを準備する
LinearLayout activityLayout = new LinearLayout(this);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT);
activityLayout.setLayoutParams(lp);
activityLayout.setOrientation(LinearLayout.VERTICAL);
activityLayout.setPadding(16, 16, 16, 16);
ViewGroup.LayoutParams tlp = new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
// Google Calendar API を呼び出す Button を準備する
mCallApiButton = new Button(this);
mCallApiButton.setText(BUTTON_TEXT);
mCallApiButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mCallApiButton.setEnabled(false);
mOutputText.setText("");
getResultsFromApi();
mCallApiButton.setEnabled(true);
}
});
activityLayout.addView(mCallApiButton);
// Google Calendar API の呼び出し結果を表示する TextView を準備する
mOutputText = new TextView(this);
mOutputText.setLayoutParams(tlp);
mOutputText.setPadding(16, 16, 16, 16);
mOutputText.setVerticalScrollBarEnabled(true);
mOutputText.setMovementMethod(new ScrollingMovementMethod());
mOutputText.setText("Click the \'" + BUTTON_TEXT + "\' button to test the API.");
activityLayout.addView(mOutputText);
// Google Calendar API の呼び出し中を表す PrgressDialog を準備する
mProgress = new ProgressDialog(this);
mProgress.setMessage("Calling Google Calendar API ...");
// ActivityにViewを設定する
setContentView(activityLayout);
// Google Calendar API の呼び出しのための認証情報を初期化する
mCredential = GoogleAccountCredential.usingOAuth2(
getApplicationContext(),
Arrays.asList(SCOPES)
).setBackOff(new ExponentialBackOff());
}
/**
* Google Calendar API の呼び出しの事前条件を確認し、条件を満たしていればAPIを呼び出す。
*
* 事前条件:
* - 有効な Google Play Services がインストールされていること
* - 有効な Google アカウントが選択されていること
* - 端末がインターネット接続可能であること
*
* 事前条件を満たしていない場合には、ユーザーに説明を表示する。
*/
private void getResultsFromApi() {
if (!isGooglePlayServicesAvailable()) {
// Google Play Services が無効な場合
acquireGooglePlayServices();
}
else if (mCredential.getSelectedAccountName() == null) {
// 有効な Google アカウントが選択されていない場合
chooseAccount();
}
else if (!isDeviceOnline()) {
// 端末がインターネットに接続されていない場合
mOutputText.setText("No network connection available.");
}
else {
new MakeRequestTask(mCredential).execute();
}
}
/**
* 端末に Google Play Services がインストールされ、アップデートされているか否かを確認する。
*
* @return 利用可能な Google Play Services がインストールされ、アップデートされている場合にはtrueを、
* そうでない場合にはfalseを返す。
*/
private boolean isGooglePlayServicesAvailable() {
GoogleApiAvailability apiAvailability =
GoogleApiAvailability.getInstance();
final int connectionStatusCode =
apiAvailability.isGooglePlayServicesAvailable(this);
return connectionStatusCode == ConnectionResult.SUCCESS;
}
/**
* ユーザーにダイアログを表示して、Google Play Services を利用可能な状態に設定するように促す。
* ただし、ユーザーが解決できないようなエラーの場合には、ダイアログを表示しない。
*/
private void acquireGooglePlayServices() {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
final int connectionStatusCode = apiAvailability.isGooglePlayServicesAvailable(this);
if (apiAvailability.isUserResolvableError(connectionStatusCode)) {
showGooglePlayServicesAvailabilityErrorDialog(connectionStatusCode);
}
}
/**
* 有効な Google Play Services が見つからないことをエラーダイアログで表示する。
*
* @param connectionStatusCode Google Play Services が無効であることを示すコード
*/
void showGooglePlayServicesAvailabilityErrorDialog(
final int connectionStatusCode
) {
GoogleApiAvailability apiAvailability = GoogleApiAvailability.getInstance();
Dialog dialog = apiAvailability.getErrorDialog(
MainActivity.this,
connectionStatusCode,
REQUEST_GOOGLE_PLAY_SERVICES
);
dialog.show();
}
/**
* Google Calendar API の認証情報を使用するGoogleアカウントを設定する。
*
* 既にGoogleアカウント名が保存されていればそれを使用し、保存されていなければ、
* Googleアカウントの選択ダイアログを表示する。
*
* 認証情報を用いたGoogleアカウントの設定には、"GET_ACCOUNTS"パーミッションを
* 必要とするため、必要に応じてユーザーに"GET_ACCOUNTS"パーミッションを要求する
* ダイアログが表示する。
*/
@AfterPermissionGranted(REQUEST_PERMISSION_GET_ACCOUNTS)
private void chooseAccount() {
// "GET_ACCOUNTS"パーミッションを取得済みか確認する
if (EasyPermissions.hasPermissions(this, Manifest.permission.GET_ACCOUNTS)) {
// SharedPreferencesから保存済みGoogleアカウントを取得する
String accountName = getPreferences(Context.MODE_PRIVATE)
.getString(PREF_ACCOUNT_NAME, null);
if (accountName != null) {
mCredential.setSelectedAccountName(accountName);
getResultsFromApi();
} else {
// Googleアカウントの選択を表示する
// GoogleAccountCredentialのアカウント選択画面を使用する
startActivityForResult(
mCredential.newChooseAccountIntent(),
REQUEST_ACCOUNT_PICKER);
}
}
else {
// ダイアログを表示して、ユーザーに"GET_ACCOUNTS"パーミッションを要求する
EasyPermissions.requestPermissions(
this,
"This app needs to access your Google account (via Contacts).",
REQUEST_PERMISSION_GET_ACCOUNTS,
Manifest.permission.GET_ACCOUNTS);
}
}
/**
* アカウント選択や認証など、呼び出し先のActivityから戻ってきた際に呼び出される。
*
* @param requestCode Activityの呼び出し時に指定したコード
* @param resultCode 呼び出し先のActivityでの処理結果を表すコード
* @param data 呼び出し先のActivityでの処理結果のデータ
*/
@Override
protected void onActivityResult(
int requestCode,
int resultCode,
Intent data
) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case REQUEST_GOOGLE_PLAY_SERVICES:
if (resultCode != RESULT_OK) {
mOutputText.setText(
"This app requires Google Play Services. Please install " +
"Google Play Services on your device and relaunch this app.");
} else {
getResultsFromApi();
}
break;
case REQUEST_ACCOUNT_PICKER:
if (resultCode == RESULT_OK && data != null && data.getExtras() != null) {
String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
if (accountName != null) {
SharedPreferences settings = getPreferences(Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_ACCOUNT_NAME, accountName);
editor.apply();
mCredential.setSelectedAccountName(accountName);
getResultsFromApi();
}
}
break;
case REQUEST_AUTHORIZATION:
if (resultCode == RESULT_OK) {
getResultsFromApi();
}
break;
}
}
/**
* Android 6.0 (API 23) 以降にて、実行時にパーミッションを要求した際の結果を受け取る。
*
* @param requestCode requestPermissions(android.app.Activity, String, int, String[])
* を呼び出した際に渡した request code
* @param permissions 要求したパーミッションの一覧
* @param grantResults 要求したパーミッションに対する承諾結果の配列
* PERMISSION_GRANTED または PERMISSION_DENIED が格納される。
*/
@Override
public void onRequestPermissionsResult(
int requestCode,
@NonNull String[] permissions,
@NonNull int[] grantResults
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
/**
* 要求したパーミッションがユーザーに承諾された際に、EasyPermissionsライブラリから呼び出される。
*
* @param requestCode 要求したパーミッションに関連した request code
* @param list 要求したパーミッションのリスト
*/
@Override
public void onPermissionsGranted(int requestCode, List<String> list) {
// 何もしない
}
/**
* 要求したパーミッションがユーザーに拒否された際に、EasyPermissionsライブラリから呼び出される。
*
* @param requestCode 要求したパーミッションに関連した request code
* @param list 要求したパーミッションのリスト
*/
@Override
public void onPermissionsDenied(int requestCode, List<String> list) {
// 何もしない
}
/**
* 現在、端末がネットワークに接続されているかを確認する。
*
* @return ネットワークに接続されている場合にはtrueを、そうでない場合にはfalseを返す。
*/
private boolean isDeviceOnline() {
ConnectivityManager connMgr =
(ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = connMgr.getActiveNetworkInfo();
return (networkInfo != null && networkInfo.isConnected());
}
/**
* 非同期で Google Calendar API の呼び出しを行うクラス。
*/
private class MakeRequestTask extends AsyncTask<Void, Void, String> {
private com.google.api.services.calendar.Calendar mService = null;
private Exception mLastError = null;
public MakeRequestTask(GoogleAccountCredential credential) {
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
mService = new com.google.api.services.calendar.Calendar
.Builder(transport, jsonFactory, credential)
.setApplicationName("Google Calendar API Android Quickstart")
.build();
}
/**
* Google Calendar API を呼び出すバックグラウンド処理。
*
* @param params 引数は不要
*/
@Override
protected String doInBackground(Void... params) {
try {
return createCalendar();
} catch (Exception e) {
mLastError = e;
cancel(true);
return null;
}
}
/**
* 選択されたGoogleアカウントに対して、新規にカレンダーを追加する。
*
* @return 作成したカレンダーのID
* @throws IOException
*/
private String createCalendar() throws IOException {
// 新規にカレンダーを作成する
com.google.api.services.calendar.model.Calendar calendar = new Calendar();
// カレンダーにタイトルを設定する
calendar.setSummary("CalendarTitle");
// カレンダーにタイムゾーンを設定する
calendar.setTimeZone("Asia/Tokyo");
// 作成したカレンダーをGoogleカレンダーに追加する
Calendar createdCalendar = mService.calendars().insert(calendar).execute();
String calendarId = createdCalendar.getId();
// カレンダー一覧から新規に作成したカレンダーのエントリを取得する
CalendarListEntry calendarListEntry = mService.calendarList().get(calendarId).execute();
// カレンダーのデフォルトの背景色を設定する
calendarListEntry.setBackgroundColor("#ff0000");
// カレンダーのデフォルトの背景色をGoogleカレンダーに反映させる
CalendarListEntry updatedCalendarListEntry =
mService.calendarList()
.update(calendarListEntry.getId(), calendarListEntry)
.setColorRgbFormat(true)
.execute();
// 新規に作成したカレンダーのIDを返却する
return calendarId;
}
@Override
protected void onPreExecute() {
mOutputText.setText("");
mProgress.show();
}
@Override
protected void onPostExecute(String output) {
mProgress.hide();
if (output == null || output.isEmpty()) {
mOutputText.setText("No results returned.");
} else {
mOutputText.setText("Calendar created using the Google Calendar API: " + output);
}
}
@Override
protected void onCancelled() {
mProgress.hide();
if (mLastError != null) {
if (mLastError instanceof GooglePlayServicesAvailabilityIOException) {
showGooglePlayServicesAvailabilityErrorDialog(
((GooglePlayServicesAvailabilityIOException) mLastError)
.getConnectionStatusCode());
} else if (mLastError instanceof UserRecoverableAuthIOException) {
startActivityForResult(
((UserRecoverableAuthIOException) mLastError).getIntent(),
MainActivity.REQUEST_AUTHORIZATION);
} else {
mOutputText.setText("The following error occurred:\n" + mLastError.getMessage());
}
} else {
mOutputText.setText("Request cancelled.");
}
}
}
}
Step 6: アプリを実行する
- メニューバーから [Run]->**[Run 'app']**を選択してください
- [Select Deployment Target]ダイアログが表示されたら、USB接続されている端末を選択して[OK]を押してください
- Android 6.0 (Marshmallow)以降の端末でアプリを実行すると、「連絡先」のパーミッションが要求されますので承諾してください
- Googleアカウントの選択画面が表示されますので、新規にカレンダーを作成するアカウントを選択してください
- Google Calendar API の呼び出し後、Googleカレンダーを起動してメニューから「更新」を実行してください
- Googleカレンダーの指定したGoogleアカウントに以下のカレンダーが追加されていることを確認してください
- 名前: CalendarTitle
- 背景色: 赤色