残念ながらDrive APIはDeprecatedとなりました。2019/12/6に完全廃止となるようです。今後は、Drive REST APIに移行するか、Firebaseなどクラウドストレージの利用を検討した方がいいでしょう。
App Folderとは
Google DriveのApp Folderは、他のアプリやユーザ自身でもデータの内容を閲覧・編集することができないアプリ専用フォルダです。そのため、アプリの設定値やゲームのセーブデータなどを保存するのにも使えそうです。ただし、ユーザのDrive容量を消費するため、大容量データを保存するのは控えた方が良いでしょう。
App Folderの操作には、Google Drive APIを使用します。この記事では、App Folderにデータを保存する実装例を紹介します。
Google Play Services SDKを利用しますので、まだダウンロードしていない場合はこちらを参照してDLしてください。
Google API Consoleでプロジェクトの作成
- Google API Consoleで新しいプロジェクトを作成します
- API Manager > 左パネルのライブラリから「Google Drive API」を有効化します
※ App Folderにはプロジェクト単位でデータが保存されるようなので、一つのアプリにつき一つのプロジェクトを作成した方が良いです。
Google API Consoleで認証情報を登録
次にGoogle API Consoleで認証情報を登録します。詳しくはこちらを読んでください。
<手順>
- 作成したプロジェクトにて、「認証情報を作成」をクリック
- OAuthクライアントIDを選択
- Androidを選択
- 名前は適当な名前を入力 (my-android-app など)
- Releaseビルド時の署名証明書のフィンガープリント(SHA1)を入力 (後述)
- アプリのパッケージ名を入力
- 「作成」をクリック
認証情報の登録にはSHA1が必要になりますが、注意しないといけないのはDebugビルド用とReleaseビルド用でSHA1が異なります。
Debugビルド用のSHA1は以下のコマンドで取得します。
$ keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore -list -v
パスワードは android です。
Releaseビルド用のSHA1も同様に取得します。
$ keytool -exportcert -keystore <YOUR_KEYSTORE_PATH> -list -v
アプリに署名するエイリアスのSHA1をコピペしてください。
なお、Releaseビルド用のSHA1は別プロジェクトで登録済みだと、重複して登録できなかったので、アプリごとにKeystoreのエイリアスを分けるなど対策の必要がありそうです。
また、同じパッケージ名で複数認証情報を登録すると不具合が生じるようです。
実際に、Releaseビルド用とDebugビルド用で同じパッケージ名で登録していたら、Releaseビルドではファイルが見つからないといったバグに悩まされました。。。(公式ドキュメントを読んでもよく分からず)
なので、僕はDebugビルドはパッケージ名を別名にして対策しています。
Androidアプリの作成
ここからはAndroidアプリの実装に入ります。Console上の設定は複雑でしたが、実装自体は比較的簡単です。
パッケージ名
上記の認証情報の作成時に入力したパッケージ名と同じにします。
build.gradleの設定
appのbuild.gradleにplay-servicesを追加します。
dependencies {
compile 'com.google.android.gms:play-services:11.0.2'
}
実装(Java)
コード例を以下に貼ります。説明はコメントを参照していただければと思います。
この例はGoogle Driveにファイルを新規生成する例です。
public class MainActivity extends AppCompatActivity
implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = "DriveSample";
private static final int REQUEST_CODE_RESOLUTION = 1;
private GoogleApiClient googleApiClient = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addScope(Drive.SCOPE_APPFOLDER) // AppFolderの利用
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
// 接続ボタンの実装
Button connectButton = (Button) findViewById(R.id.connectButton);
connectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (googleApiClient != null && !googleApiClient.isConnected()) {
// 接続開始
googleApiClient.connect();
}
}
});
// 接続解除ボタンの実装
Button disconnectButton = (Button) findViewById(R.id.disconnectButton);
disconnectButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (googleApiClient != null && googleApiClient.isConnected()) {
// 接続解除
googleApiClient.disconnect();
}
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE_RESOLUTION && resultCode == RESULT_OK) {
// アクセス承認を得たので再接続開始
googleApiClient.connect();
}
}
@Override
public void onConnected(@Nullable Bundle bundle) {
// 接続完了したのでDriveに新しいコンテンツを生成する
Drive.DriveApi.newDriveContents(googleApiClient)
.setResultCallback(driveContentsResultCallback);
}
@Override
public void onConnectionSuspended(int i) {
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult result) {
if (!result.hasResolution()) {
// show the localized error dialog.
GoogleApiAvailability.getInstance().getErrorDialog(this, result.getErrorCode(), 0).show();
return;
}
try {
// ユーザにGoogle Driveへのアクセス承認ダイアログを表示する
// onActivityResultに結果が通知されるので第二引数に指定したcodeで判別する
result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
}
catch (IntentSender.SendIntentException e) {
// Exception while starting resolution activity
}
}
private final ResultCallback<DriveContentsResult> driveContentsResultCallback = new ResultCallback<DriveContentsResult>() {
@Override
public void onResult(@NonNull DriveContentsResult result) {
if (!result.getStatus().isSuccess()) {
// Failed to create new content
return;
}
final DriveContents driveContents = result.getDriveContents();
OutputStream outputStream = driveContents.getOutputStream();
Writer writer = new OutputStreamWriter(outputStream);
try {
// 適当なJSONデータを書き込む
JSONObject jsonObject = new JSONObject();
jsonObject.put("key1", true)
.put("key2", 100.0)
.put("key3", 123);
writer.write(jsonObject.toString());
writer.close();
}
catch (JSONException | IOException e) {
}
// "sample.json"というファイル名でファイル保存する
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle("sample.json")
.setMimeType("text/plain")
.build();
Drive.DriveApi.getAppFolder(googleApiClient)
.createFile(googleApiClient, changeSet, driveContents)
.setResultCallback(fileCallback);
}
};
private final ResultCallback<DriveFileResult> fileCallback = new ResultCallback<DriveFileResult>() {
@Override
public void onResult(@NonNull DriveFileResult result) {
if (!result.getStatus().isSuccess()) {
// Failed to create a file
return;
}
// ファイルの作成に成功するとDriveIdが発行される
// このDriveIdを保存しておくと、次回以降DriveIdを使ってファイル検索できる
Log.d(TAG, "Success to create a file. " + result.getDriveFile().getDriveId());
}
};
}
この他にもファイルの検索・取得・削除といった操作が可能ですので、公式ドキュメントを参照してみてください。
まとめ
App Folderの使い方を紹介しました。公式ドキュメントにわかりづらい部分もありますが、アプリデータのバックアップなどにも利用できるので、ぜひ使ってみてください。