2014年11月25日火曜日

Android : Google Drive 上のファイルの作成・読み込み(一般フォルダ編)

Google Drive 上のファイルとして見えるファイルを作成、読み込みするアプリを開発する方法のメモです。
共通する部分は多数ありますが、アプリケーション専用フォルダ(APP_FOLDER)ではありません。
また、ファイルの編集(すでにあるファイルを読み込み、その内容を変えて保存、というのもこの記事では扱っていません。


準備1. サービスの作成、API の有効化

Set up the game in the Google Play Developer Console を参考にゲームサービスを作成し、作成したプロジェクトの Google Developers Console で Drive API を有効にします。


準備2. GoogleApiClient の作成

まずは GoogleApiClient を作成し、connect を呼んでログインしておく必要があります。
ここで必要な API は Drive.API, Scope は Drive.SCOPE_FILE です。
(ちなみにアプリケーション専用フォルダへの保存の場合はスコープに SCOPE_APPFOLDER が必要になります)
また、以降サンプル内で定義されていない変数・定数についてはクラスのメンバ変数などとしてどこかで定義されていると思ってください。
mGoogleApiClient = new GoogleApiClient.Builder(this)
        .addConnectionCallbacks(new GoogleApiClient.ConnectionCallbacks() {
            @Override
            public void onConnected(Bundle bundle) {
                Log.i(LOGTAG, "onConnected");
            }

            @Override
            public void onConnectionSuspended(int i) {
                Log.i(LOGTAG, "onConnectionSuspended:" + i);
            }
        })
        .addOnConnectionFailedListener(new GoogleApiClient.OnConnectionFailedListener() {
            @Override
            public void onConnectionFailed(ConnectionResult connectionResult) {
                Log.i(LOGTAG, "onConnectionFailed:" + connectionResult.toString());
                if(connectionResult.hasResolution()) {
                    try {
                        // 初回起動時などで未認証の場合などはこちらにきます
                        connectionResult.startResolutionForResult(MainActivity.this, GOOGLE_API_CLIENT_RESOLUTION);
                    } catch (Exception e) {
                        Log.e(LOGTAG, e.toString());
                    }
                } else{
                    Log.e(LOGTAG, "hasNoResolution()");
                    int errorCode = connectionResult.getErrorCode();
                    Dialog dialog = GooglePlayServicesUtil.getErrorDialog(errorCode, MainActivity.this, GOOGLE_API_CLIENT_ERROR);
                    if( dialog != null){
                        dialog.show();
                    } else {
                        Log.e(LOGTAG, "no dialog");
                    }
                }
            }
        })
        .addApi(Drive.API).addScope(Drive.SCOPE_FILE)
        .build();
mGoogleApiClient.connect();
startResolutionForResult で OK が返ってきた時に再接続を試みるために onActivityResult に以下のようなコードを書き加えておきます。
※ startResolutionForResult は Activity から startActivity される扱いになるので、Activity の onActivityResult で処理必要があります。Fragment 内から呼んで、Fragment の onActivityResult で処理したい場合は、Activity の onActivityResult からその Fragment の onActivityResult を呼ぶよう明示的にコードを書く必要があります(他の Activity 起動系 API も同様です)。
if(requestCode == GOOGLE_API_CLIENT_RESOLUTION){
    switch (resultCode) {
        case Activity.RESULT_OK:
            mGoogleApiClient.connect();
            break;
        default:
            Log.e(LOGTAG, "" + resultCode);
    }
}


ファイルの作成

DriveApi.newDriveContents に設定したコールバック関数内で、DriveApi.newCreateFileAcitivtyBuilder() にメタデータや初期データを設定、アクティビティを呼ぶ、という流れです。

ResultCallback<DriveApi.DriveContentsResult> newDriveContentsCallback = new
    ResultCallback<DriveApi.DriveContentsResult>() {
        @Override
        public void onResult(DriveApi.DriveContentsResult result) {
            // MimeType は適宜変更する。他にも必要な項目があれば set 関数で set する。
            // setTitle で初期タイトルくらいは決めてあげると良さそう
            MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
                    .setMimeType("text/plain").build();

            DriveContents contents = result.getDriveContents();
            // OutputStream を取得して初期データを書き込む。例として、適当な文字列を書き込む。
            try {
                OutputStream os = contents.getOutputStream();
                String hello = "Hello World!";
                os.write(hello.getBytes());
                os.close();
            } catch (Exception e){
                Log.e(LOGTAG, e.toString());
            }

            // デフォルトで用意された UI を表示
            IntentSender intentSender = Drive.DriveApi
                    .newCreateFileActivityBuilder()
                    .setInitialMetadata(metadataChangeSet)
                    .setInitialDriveContents(contents)
                    .build(mGoogleApiClient);
            try {
                startIntentSenderForResult(
                        intentSender, REQUEST_CODE_CREATOR, null, 0, 0, 0);
            } catch (IntentSender.SendIntentException e) {
                Log.w(LOGTAG, "Unable to send intent", e);
            }
        }
    };

Drive.DriveApi.newDriveContents(mGoogleApiClient).setResultCallback(newDriveContentsCallback);

onActivityResult に以下のようなコードを書いておくと作成された Google Drive ファイルの DriveId が取得可能です(作りっぱなしでよいなら取得する必要はありません)。
if(requestCode == REQUEST_CODE_CREATOR){
    Log.i(LOGTAG,"REQUEST_CODE_CREATOR:" + resultCode);
    if(resultCode == RESULT_OK) {
        DriveId driveId = data.getParcelableExtra(CreateFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);
        Log.i(LOGTAG, "Drive Id:" + driveId.toString());
    }
}


ファイルの読み込み

DriveApi.newOpenFileActivityBuilder() を使用してアクティビティを呼び出し、onActivityResult で DriveId を取得、DriveId からファイルを開いて内容を読み込むという流れです。
まずはファイル選択ダイアログの呼び出し。
// MimeType でフィルタリングされる
IntentSender intentSender = Drive.DriveApi
        .newOpenFileActivityBuilder()
        .setMimeType(new String[] { "text/plain" })
        .build(mGoogleApiClient);
try {
    startIntentSenderForResult(
            intentSender, REQUEST_CODE_OPENER, null, 0, 0, 0);
} catch (IntentSender.SendIntentException e) {
    Log.w(LOGTAG, "Unable to send intent", e);
}

以下のコードは onActivityResult 内という想定です。DriveId からファイル内容を取得します。
if(requestCode == REQUEST_CODE_OPENER){
    if(resultCode == RESULT_OK){
        DriveId driveId = data.getParcelableExtra(
                OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);

        // 取得した driveId からファイルの内容を取得。
        // プログレスを取得して何か行う場合は file.open の第三引数にリスナーを設定する。
        DriveFile file = Drive.DriveApi.getFile(mGoogleApiClient, driveId);
        file.open(mGoogleApiClient, DriveFile.MODE_READ_ONLY, null).setResultCallback(new ResultCallback<DriveApi.DriveContentsResult>() {
            @Override
            public void onResult(DriveApi.DriveContentsResult driveContentsResult) {
                if(driveContentsResult.getStatus().isSuccess()){
                    DriveContents driveContents = driveContentsResult.getDriveContents();
                    // DriveContents の getInputStream で内容を取得する。
                    // 以下はファイルの内容がテキストであることを想定した例。
                    BufferedReader reader = new BufferedReader(
                            new InputStreamReader(driveContents.getInputStream()));
                    StringBuilder builder = new StringBuilder();
                    String contents = null;
                    String line;
                    try {
                        while ((line = reader.readLine()) != null) {
                            builder.append(line);
                        }
                        contents = builder.toString();
                    } catch (IOException e) {
                        Log.e(LOGTAG, "IOException while reading from the stream", e);
                    }
                    // 読み込むだけで内容に変更はなし。discard を呼んで閉じておく(意味は無いかも?)
                    driveContents.discard(mGoogleApiClient);

                    Toast.makeText(MainActivity.this, contents, Toast.LENGTH_LONG).show();
                }
            }
        });
    }
}


ちなみに、認証画面をもう一度出したい場合などで、認証を取り消したいときは このリンク先からアプリを 選択してアクセス権を取り消します。

参考
googledrive/android-demos
com.google.android.gms.drive
Google Drive Android API : Create files
Google Drive Android API : Working with File Contents

0 件のコメント:

コメントを投稿