GoogleDriveアクセスでのAndroid ApiとAndroid REST Apiの共存
GoogleDriveのDeskTop版が終了する事と関連してのことだと思いますが、GoogleDriveAPIのDriveApiが非推奨となり代わりにDriveClientを利用するようにとアナウンスされています。
今作成中のソフトがすべてDriveApiを使っていたので気軽な気持ちで一連のルーチンを変更していったのですが、その際、どうしても解決できない問題に直面してしまいました。 皆さんのお知恵を拝借できないでしょうか?
GoogleDriveへのアクセスにはGoogle Drive Android APIを使う方法とGoogle Drive REST APIを使う方法の2種類があります。
DriveApiが非推奨になる前はこの両方を状況に合わせて使い分けていました。
GoogleDriveにワークホルダーを作成するときはDriveApiを使い、そこにコンテンツを保存するときはRESTApiを使ってパーミッションを変更する、と言うようなことをしていました。
ところが、ここをDriveClientまたはDriveResourceClientを取得してホルダーを作成する方法に変更したところ、GoogleAccountを作成した直後(つまり新しいGoogle Account)の場合は問題なくホルダーを作成できるのですが、その後に一度でもGoogle Drive REST APIを利用したアクセスを行ったら、2度とGoogle Drive Android APIでアクセスできなくなると言う不思議な現象に直面してしまいました。
if( mGoogleSignInAccount != null ) {
mDriveClient = Drive.getDriveClient(getActivity().getApplicationContext(),mGoogleSignInAccount);
mDriveResourceClient = Drive.getDriveResourceClient(getActivity().getApplicationContext(),mGoogleSignInAccount);
onDriveClientReady();
} else {
signIn();
}
ここで得られたDriveResourceClientからホルダーを作成しました。
mDriveResourceClient
.getRootFolder()
.continueWithTask(new Continuation<DriveFolder, Task<DriveFolder>>() {
@Override
public Task<DriveFolder> then(@NonNull Task<DriveFolder> task) {
DriveFolder parentFolder = task.getResult();
MetadataChangeSet changeSet = new MetadataChangeSet.Builder()
.setTitle(APPLICATION_HOLDER)
.setDescription(AppLICATION_HOLDER_DESCRIPTION)
.setMimeType(DriveFolder.MIME_TYPE)
.build();
Task<DriveFolder> result = mDriveResourceClient.createFolder(parentFolder,changeSet);
return result;
}
})
.addOnSuccessListener(getActivity(), new OnSuccessListener<DriveFolder>() {
@Override
public void onSuccess(DriveFolder driveFolder) {
Log.i(TAG, "onSuccess: " + driveFolder.getDriveId().encodeToString());
createImageFolder(driveFolder);
}
})
.addOnFailureListener(getActivity(), new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.e(TAG, "onFailure: Can't create Folder. Named " + APPLICATION_HOLDER );
mProgressDialog.dismiss();
}
});
SignInは以下のように作成しています。(長くなるので一部省略)
protected void signIn() {
Set<Scope> requiredScopes = new HashSet<>(2);
requiredScopes.add(Drive.SCOPE_FILE);
requiredScopes.add(Drive.SCOPE_APPFOLDER);
requiredScopes.add(SCOPE_CONTACTS_READ);
requiredScopes.add(SCOPE_EMAIL);
mGoogleSignInAccount = GoogleSignIn.getLastSignedInAccount(getContext().getApplicationContext());
if(mGoogleSignInAccount == null || !mGoogleSignInAccount.getGrantedScopes().containsAll(requiredScopes)) {
GoogleSignInOptions signInOptions =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Drive.SCOPE_FILE,Drive.SCOPE_APPFOLDER,SCOPE_CONTACTS_READ,SCOPE_EMAIL)
.requestEmail()
.requestId()
.setAccountName(mGoogleSignInAccount.getAccount())
.build();
mGoogleSignInClient = GoogleSignIn.getClient(getActivity().getApplicationContext(),signInOptions);
startActivityForResult(mGoogleSignInClient.getSignInIntent(),RC_SIGN_IN);
}
} else {
GoogleSignInOptions signInOptions =
new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(Drive.SCOPE_FILE,Drive.SCOPE_APPFOLDER,SCOPE_CONTACTS_READ,SCOPE_EMAIL)
.requestEmail()
.requestId()
.setAccountName(mGoogleSignInAccount.getAccount().name)
.build();
mGoogleSignInClient = GoogleSignIn.getClient(getActivity().getApplicationContext(), signInOptions);
startActivityForResult(mGoogleSignInClient.getSignInIntent(), RC_SIGN_IN);
}
}
サインイン処理後のonActivityResultは
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
switch (requestCode) {
case RC_SIGN_IN :
if( resultCode != Activity.RESULT_OK) {
Log.w(TAG, "Sign In failed. ");
return;
}
Task<GoogleSignInAccount> getAccountTask =
GoogleSignIn.getSignedInAccountFromIntent(data);
if( getAccountTask.isSuccessful()) {
try {
mGoogleSignInAccount = getAccountTask.getResult(ApiException.class);
onSignInSuccess();
} catch (ApiException e) {
e.printStackTrace();
Log.w(TAG, "Fail to get task results.:" + e.getMessage());
return;
}
} else {
Exception e = getAccountTask.getException();
if( e != null ) {
Log.w(TAG, "Sign In failed. " );
} else {
Log.w(TAG, "Sign In Failed. Error Message = " + e.getMessage() );
}
}
break;
//長くなるので以下省略
}
}
先にも書きましたが、このようにしてホルダー作成することは、ユーザーアカウントがまだGoogleDriveに一度もアクセスしていない場合には成功します。
このあと、このホルダーにコンテンツをアップロードしそのコンテンツに対してPermissionを設定するために、
new GoogleDriveUtils.MakeCreatePermission(
getBaseContext(),
fieldId,
null,
permission,
getGoogleAccountCredential()) {
@Override
protected Permission doInBackground(Void... params) {
Permission result = null;
try {
result = mService.permissions()
.create(fieldId,newPermission)
.setFields("id,kind,emailAddress,role,type,domain")
.execute();
if(mGid != null)
StorePermissionToDb(mContext,mGid,result);
} catch (IOException e) {
e.printStackTrace();
mLastError = e;
cancel(true);
}
return result;
}
@Override
protected void onPostExecute(Permission permission) {
if( mProgressDialog != null ) {
mProgressDialog.dismiss();
}
finish();
}
@Override
protected void onCancelled() {
if (mLastError instanceof UserRecoverableAuthIOException) {
startActivityForResult(
((UserRecoverableAuthIOException) mLastError).getIntent(),
GoogleDriveUtils.REQUEST_AUTHORIZATION);
}
super.onCancelled();
}
}.execute();
と言うようにGoogle Drive REST APIを使ってパーミッションを変更する処理を行うと、初回はUserRecoverableAuthIOExceptionをキャッチして、ユーザにGoogleDriveへのアクセスを許可するかを問い合わせる処理が実行されます。
問題はこの後で、デバッグのためにここでアプリケーションを再起動して、WorkFolderを検索したり削除したりする処理をGoogle Drive Android APIを用いて行おうとすると、
com.google.android.gms.common.api.ApiException: 17: API: Drive.API_CONNECTIONLESS is not available on this device
と言うエラーが発生して二度と接続できなくなります。
まとめると、
1)GoogleAccountが作成直後であれば、Google Drive Android APIでもGoogle Drive REST APIでも処理可能でホルダー作成もできる。
2)Google Drive Android APIでホルダーを作成した後に、Google Drive REST APIでそのホルダーの何らかの処理を行おうとすると、UserRecoverableAuthIOExceptionが発生するので、そこからリカバリーは行える。
3)Google Drive REST APIでホルダーを作成した場合、Google Drive Android APIでアクセスすると、com.google.android.gms.common.api.ApiException: 17: API: Drive.API_CONNECTIONLESS is not available on this device が発生してその後リカバーできなくなる。
4)Google Drive Android APIでホルダーであっても、その後Google Drive REST APIでアクセスしUserRecoverableAuthIOExceptionを経由してリカバーすると、Google Drive Android APIでアクセスすることが出来なくなる。
と言う事になります。
私が知りたいのは、3)または4)の時にリカバリーして、Drive.API_CONNECTIONLESS を解消できないかと言う事です。
かなり長くなってしまいましたが、どなたかご教示頂けないでしょうか。
よろしくお願いいたします。