How to update files to Google Drive by API.
Google Drive Android API
Getting Start から順次作業を進めます。
Get an Android certificate and register your application
アプリごとにGoogle Driveにアクセスするための認証しなさい、という事です。認証はアプリに付けられた署名のフィンガープリントによって行います。注意すべきは、デバッグ用の署名と本番用の署名は違う、ということです。ご存知のように、アプリをEclipse等から起動した場合、デバッグ用の署名が自動で付けられます。これは本番用に(Android Tool⇛署名付きアプリをエクスポート)アプリを作成する際に使用する署名とは違うものです。
ですので、Google Developers Consoleに登録する際は、デバッグ用と本番用の2つを登録してしまったほうが良いと思います。
また、フィンガープリントをGUIで見ることができるEclipseプラグインが有ります。
http://keytool.sourceforge.net/
Create a Proguard Exception
Proguardの設定をマニュアル通りに行っても、エラーが出てコンパイルできませんでした。他にも例外処理を加えなければならないとの情報もありましたが、結局Proguardをオフにしてコンパイルしました。
Creating files
ここで書かれているコードは、空のファイルを作成するだけで、ローカルファイルをアップロードする訳ではありません。uploadFile(.., .., .., URI)なんてメソッドがあればいいのですが・・・。この情報が見つからなくて苦労しました。SDKを使う昔の方法や、POSTする方法しか見つからず。こんな方法しかないの?というコードになってしまいました。
ContentsResultCallback
↓
fileCallback
↓
ApiClientAsyncTaskにDriveFileを渡す
↓
DriveFileをMODE_WRITE_ONLYで開いてOutputStreamで書き込む
また、Drive上のファイル名やフォルダ名でアクセスすることは、このAPIでは不可能です。なんと不便なのでしょう!DriveIdという任意の文字列を介してアクセスすることになります。
例)
カメラで撮った画像をそのままGoogle Driveの指定したフォルダにアップロードするコードです。
フォルダのDriveIdは、下記のサンプルを参考に、ユーザーが指定するようにしました。
https://github.com/googledrive/android-demos/blob/master/src/com/google/android/gms/drive/sample/demo/PickFolderWithOpenerActivity.java
DriveIdはPreferenceに保存しておきます。もしフォルダが見つからない場合は、ルートフォルダに保存します。
また、PrefUtilは私の作ったクラスで、Preferenceを読み込み/書き込みするだけなので、一般的なコードに書き換えれば動作します。
(1) Activity(implements Camera.PictureCallback, GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener)
googleApiClient.connect()が成功するとonConnected()に飛びます。
static GoogleApiClient googleApiClient;
static String uploadFileName;
static byte[] uploadData;
static DriveId folderDriveId;
@Override
public void onPictureTaken(byte[] data, Camera camera) {
try {
String myPath = Environment.getExternalStorageDirectory().getPath() + "/SecEye";
File dir = new File(myPath);
if (!dir.exists()){
dir.mkdirs();
}
Calendar calendar = Calendar.getInstance(Locale.getDefault());
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy_MMdd_HHmm_ss", Locale.getDefault());
String myFileName = simpleDateFormat.format(calendar.getTime()) + ".jpg";
String myFile = myPath + "/" + myFileName;
BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(myFile));
outputStream.write(data);
outputStream.close();
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
Uri contentUri = Uri.fromFile(new File(myFile));
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
uploadFileName = myFileName;
uploadData = data;
googleApiClient = new GoogleApiClient.Builder(this)
.addApi(Drive.API)
.addScope(Drive.SCOPE_FILE)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
googleApiClient.connect();
} catch (Exception e) {
Log.i("onPictureTaken", "file_error");
} finally {
Log.i("onPictureTaken", "End");
finish();
}
}
(2) API関係onConnected()で指定したフォルダを探しに行きます。ただし、DriveIdが空欄だと落ちるので、その場合はルートフォルダにファイルを作成する道をとります。newContentsのCallbackを指定フォルダ用とルートフォルダ用に分けました。
フォルダが無事見つかった場合は、フォルダのDriveIdを利用してDriveFolderを作成し、そこにファイルを作成します。
ファイルが指定フォルダ又はルートフォルダに作成されると、fileCallbackへ進みます。
// Google Drive Upload Tools
@Override
public void onConnectionFailed(ConnectionResult arg0) {
finish();
}
@Override
public void onConnected(Bundle connectionHint) {
PreferenceUtil prefUtil = new PreferenceUtil(this);
if (!prefUtil.GetString("folderDriveId").isEmpty()) {
Drive.DriveApi.fetchDriveId(googleApiClient, prefUtil.GetString("folderDriveId"))
.setResultCallback(idCallback);
} else {
Drive.DriveApi.newContents(googleApiClient)
.setResultCallback(contentsOnRootCallback);
}
}
@Override
public void onConnectionSuspended(int cause) {
//finish();
}
final private ResultCallback<DriveIdResult> idCallback = new ResultCallback<DriveIdResult>() {
@Override
public void onResult(DriveIdResult result) {
if (!result.getStatus().isSuccess()) {
Drive.DriveApi.newContents(googleApiClient)
.setResultCallback(contentsOnRootCallback);
} else {
folderDriveId = result.getDriveId();
Drive.DriveApi.newContents(googleApiClient)
.setResultCallback(contentsCallback);
}
}
};
// create in selected folder
final private ResultCallback<ContentsResult> contentsCallback = new ResultCallback<ContentsResult>() {
@Override
public void onResult(ContentsResult result) {
if (result.getStatus().isSuccess()) {
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(uploadFileName)
.setMimeType("image/jpg")
.setStarred(false).build();
DriveFolder folder = Drive.DriveApi.getFolder(googleApiClient, folderDriveId);
folder.createFile(googleApiClient, changeSet, result.getContents())
.setResultCallback(fileCallback);
}
}
};
// create in root folder if (unknown folder/not selected)
final private ResultCallback<ContentsResult> contentsOnRootCallback = new ResultCallback<ContentsResult>() {
@Override
public void onResult(ContentsResult result) {
if (result.getStatus().isSuccess()) {
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(uploadFileName)
.setMimeType("image/jpg")
.setStarred(false).build();
Drive.DriveApi.getRootFolder(googleApiClient).createFile(googleApiClient, changeSet, result.getContents())
.setResultCallback(fileCallback);
}
}
};
// two contentsCallbacks return one fileCallback
final private ResultCallback<DriveFileResult> fileCallback = new ResultCallback<DriveFileResult>() {
@Override
public void onResult(DriveFileResult result) {
if (result.getStatus().isSuccess()) {
DriveFile file = result.getDriveFile();
new EditContentsAsyncTask(CameraActivity.this).execute(file);
}
}
};
(3) アップロード作成した空のファイルを開き、OutputStreamを使って画像データをアップロードします。アップロードが終わったらGoogleApiClientをdisconnectしていますが、このタイミングは設計によりけりです。
public class EditContentsAsyncTask extends ApiClientAsyncTask<DriveFile, Void, Boolean> {
public EditContentsAsyncTask(Context context) {
super(context);
}
@Override
protected Boolean doInBackgroundConnected(DriveFile... args) {
DriveFile file = args[0];
try {
ContentsResult contentsResult = file.openContents(
getGoogleApiClient(), DriveFile.MODE_WRITE_ONLY, null).await();
if (!contentsResult.getStatus().isSuccess()) {
return false;
}
OutputStream outputStream = contentsResult.getContents().getOutputStream();
outputStream.write(uploadData);
com.google.android.gms.common.api.Status status = file.commitAndCloseContents(
getGoogleApiClient(), contentsResult.getContents()).await();
try {
if (googleApiClient.isConnected() || googleApiClient.isConnecting()) {
googleApiClient.disconnect();
}
} catch (Exception e) {
} finally {
googleApiClient = null;
}
return status.getStatus().isSuccess();
} catch (IOException e) {
}
return false;
}
@Override
protected void onPostExecute(Boolean result) {}
}
0 件のコメント:
コメントを投稿