Google APIs Client Library for JavaからGoogle Drive APIを使用する

AndroidからGoogle DriveのAPIを使用する場合、Google Play Serviceに統合されたGoogle Drive APIを利用するのが楽です。
が、現状では自アプリ作成したファイル/フォルダの操作のみしかできないため、それ以外のことをする場合にはGoogle APIs Client Library for Javaを利用する必要があります。

https://developers.google.com/drive/android/auth

上記にも接続と認証を行う際のスニペットが載っていますが、不完全で分かりにくかったため利用方法のメモ書きです。

##下準備

### アプリケーション登録
Google Developers Consoleで作成するAndroidアプリの登録を行っておきます。
以下を参照して、Client IDを新規追加してください。
https://developers.google.com/drive/android/auth

### jar
以下からLibraryのダウンロード（Android Studioの場合はimport設定）を行います。
https://developers.google.com/api-client-library/java/apis/drive/v2

Eclipseでの開発の場合、ダウンロード後のzipを解凍し、
readme.htmlに記載されている通り、以下のjarをlibs下に配置します。

google-api-services-drive-v2-rev123-1.18.0-rc.jar （※）
google-api-client-1.18.0-rc.jar
google-oauth-client-1.18.0-rc.jar
google-http-client-1.18.0-rc.jar
jsr305-1.3.9.jar
google-http-client-gson-1.18.0-rc.jar
gson-2.1.jar
google-api-client-android-1.18.0-rc.jar
google-http-client-android-1.18.0-rc.jar

※のみをプロジェクトのプロパティからJava Build PathのLibrariesに追加する

### 依存ライブラリ

Google Play Service用ライブラリプロジェクトに依存が必要です。
http://developer.android.com/google/play-services/setup.html

### Permission
以下を追加します。

```AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
```


### MetaData
Google Play Serviceを利用するために以下を追加します。

```AndroidManifest.xml
   <application
      ...>
       <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
   </appliction>
```
 

## 実装

以下の流れで接続と認証、API呼び出しを行います。

1. アカウント選択ダイアログ呼び出し用のIntentを発行
2. Drive APIを呼び出す
3. 2のとき、認証が必要な場合にExceptionが発生するので認証用のIntentを発行
4. 認証後、Drive APIを再呼び出し

1については、一度接続したアカウント名をPreference等に保存しておき、設定することも可能です。


```java:Activity.java
package com.example.googledriveapisample;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

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.GoogleAuthIOException;
import com.google.api.client.googleapis.extensions.android.gms.auth.UserRecoverableAuthIOException;
import com.google.api.client.json.gson.GsonFactory;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.api.services.drive.Drive.Files;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;

import android.accounts.AccountManager;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;

public class MainActivity extends Activity {

	private static final String TAG = MainActivity.class.getSimpleName();
	
    private static final int REQUEST_ACCOUNT_PICKER = 1;
    
    private static final int REQUEST_AUTHORIZATION = 2;

    private GoogleAccountCredential mCredential;
	
	private Drive mDriveService;
	
	private List<File> mFileList = new ArrayList<File>();
	
	private Handler mHandler;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		mHandler = new Handler();
	}
	
	@Override
	protected void onStart() {
		super.onStart();
		
		setupDriveService();
	}
	
	private void setupDriveService() {
        mCredential = GoogleAccountCredential.usingOAuth2(this, Arrays.asList(DriveScopes.DRIVE));
    	if (mDriveService == null) {
    		// 保存済みのアカウント名を取得（xxx@gmail.com）
    		String accountName = getSavedAccountName();
    		if (accountName.isEmpty()) {
    			// アカウント名がなければアカウント選択ダイアログ表示用のIntentを発行
    			startActivityForResult(mCredential.newChooseAccountIntent(), REQUEST_ACCOUNT_PICKER);
    		} else {
    			// アカウント名がある場合
    			mCredential.setSelectedAccountName(accountName);
    			// DriveAPI用のインスタンス生成
    			mDriveService = getDriveService();
    			// Drive API呼び出し
    			callDriveApi();
    		}
    	}
	}
	
	private void callDriveApi() {
		// 非UIスレッドで呼び出す
		// （AsyncTask等でAPI呼び出し用の基底クラスをつくって認証が必要な場合の処理を実施したほうが良い）
		(new Thread(new Runnable() {
			@Override
			public void run() {
				mFileList.clear();
				try {
					// ファイル一覧を取得したい場合
					Files.List request = mDriveService.files().list();
					// クエリ文字列をセット（以下はMIMETYPEを指定）
					request.setQ("mimeType = 'audio/mpeg'");

					do {
						// API呼び出し
						FileList files = request.execute();
						// 取得したFileリストを保持用のメンバーにセット
						mFileList.addAll(files.getItems());
						// 全アイテムを取得するために繰り返し
						request.setPageToken(files.getNextPageToken());
					} while (request.getPageToken() != null
							&& request.getPageToken().length() > 0);

					for (File f : mFileList) {
						Log.d(TAG, f.getTitle());
					}
					
				} catch (UserRecoverableAuthIOException e) {
					// 認証が必要な場合に発生するException
					// これが発生したら認証のためのIntent発行を行い、認証後、DriveAPIを再呼び出しする
					final Intent authIntent = e.getIntent();
					mHandler.post(new Runnable() {
						@Override
						public void run() {
							startActivityForResult(authIntent, REQUEST_AUTHORIZATION);
						}
					});
				} catch (GoogleAuthIOException e) {
					// Developer ConsoleでClientIDを設定していない場合に発生する
					e.printStackTrace();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		})).start();
	}
	
	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		switch (requestCode) {
		case REQUEST_ACCOUNT_PICKER:
			if ((resultCode == RESULT_OK) && (data != null)) {
				// 選択されたアカウント名を取得
				String accountName = data.getStringExtra(AccountManager.KEY_ACCOUNT_NAME);
	            if (accountName != null) {
	                mCredential.setSelectedAccountName(accountName);
	                mDriveService = getDriveService();
	                // Drive API呼び出し
	                callDriveApi();
	                // 必要ならアカウント名を保存しておく
	                saveAccountName(accountName);
	            }			
	        } else {
				// エラー処理
			}
			break;
			
		case REQUEST_AUTHORIZATION:
			if (resultCode == RESULT_OK) {
				// 認証に成功したのでDrive APIを再呼び出し
				callDriveApi();
			} else {
				// エラー処理
			}
			break;
		}
	}
		
	private Drive getDriveService() {
    	return new Drive.Builder(AndroidHttp.newCompatibleTransport(), new GsonFactory(), mCredential).build();
	}
	
	private String getSavedAccountName() {
		SharedPreferences prefs = getSharedPreferences("prefs_name", Context.MODE_PRIVATE);
		return prefs.getString("key_account_name", "");
	}
	
	private void saveAccountName(final String accountName) {
		SharedPreferences prefs = getSharedPreferences("prefs_name", Context.MODE_PRIVATE);
		SharedPreferences.Editor editor = prefs.edit();
		editor.putString("key_account_name", accountName);
		editor.commit();
	}
}
```

## 参考
https://developers.google.com/api-client-library/java/apis/drive/v2

Google Play Service用の認証方法との住み分けについて
http://stackoverflow.com/questions/22142641/access-to-google-api-googleaccountcredential-usingoauth2-vs-googleauthutil-get