

GoogleDriveのDeskTop版が終了する事と関連してのことだと思いますが、GoogleDriveAPIのDriveApiが非推奨となり代わりにDriveClientを利用するようにとアナウンスされています。

今作成中のソフトがすべてDriveApiを使っていたので気軽な気持ちで一連のルーチンを変更していったのですが、その際、どうしても解決できない問題に直面してしまいました。
皆さんのお知恵を拝借できないでしょうか？

GoogleDriveへのアクセスにはGoogle Drive Android APIを使う方法とGoogle Drive REST
APIを使う方法の２種類があります。  
DriveApiが非推奨になる前はこの両方を状況に合わせて使い分けていました。

GoogleDriveにワークホルダーを作成するときはDriveApiを使い、そこにコンテンツを保存するときはRESTApiを使ってパーミッションを変更する、と言うようなことをしていました。

ところが、ここをDriveClientまたはDriveResourceClientを取得してホルダーを作成する方法に変更したところ、GoogleAccountを作成した直後（つまり新しいGoogle
Account)の場合は問題なくホルダーを作成できるのですが、その後に一度でもGoogle Drive REST
APIを利用したアクセスを行ったら、２度とGoogle Drive Android APIでアクセスできなくなると言う不思議な現象に直面してしまいました。

[code]

    if( mGoogleSignInAccount != null ) {
        mDriveClient = Drive.getDriveClient(getActivity().getApplicationContext(),mGoogleSignInAccount);
        mDriveResourceClient = Drive.getDriveResourceClient(getActivity().getApplicationContext(),mGoogleSignInAccount);
        onDriveClientReady();
    } else {
        signIn();
    }
    
[/code]

ここで得られたDriveResourceClientからホルダーを作成しました。

[code]

    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();
                }
            });
    
[/code]

SignInは以下のように作成しています。(長くなるので一部省略）

[code]

    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);
        }
    }
    
[/code]

サインイン処理後のonActivityResultは

[code]

    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;
          //長くなるので以下省略
          }
     }
    
[/code]

先にも書きましたが、このようにしてホルダー作成することは、ユーザーアカウントがまだGoogleDriveに一度もアクセスしていない場合には成功します。

このあと、このホルダーにコンテンツをアップロードしそのコンテンツに対してPermissionを設定するために、

[code]

    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();
    
[/code]

と言うように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  
と言うエラーが発生して二度と接続できなくなります。

まとめると、

１）GoogleAccountが作成直後であれば、Google Drive Android APIでもGoogle Drive REST
APIでも処理可能でホルダー作成もできる。  
２）Google Drive Android APIでホルダーを作成した後に、Google Drive REST
APIでそのホルダーの何らかの処理を行おうとすると、UserRecoverableAuthIOExceptionが発生するので、そこからリカバリーは行える。  
３）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 が発生してその後リカバーできなくなる。  
４）Google Drive Android APIでホルダーであっても、その後Google Drive REST
APIでアクセスしUserRecoverableAuthIOExceptionを経由してリカバーすると、Google Drive Android
APIでアクセスすることが出来なくなる。  
と言う事になります。

私が知りたいのは、３）または４）の時にリカバリーして、Drive.API_CONNECTIONLESS を解消できないかと言う事です。

かなり長くなってしまいましたが、どなたかご教示頂けないでしょうか。  
よろしくお願いいたします。


CC BY-SA 3.0
2018-04-22T06:59:40.103
43471
2018-04-22T08:09:38.103
2018-04-22T08:09:38.103
3060
20466
question
google-drive-sdk
GoogleDriveアクセスでのAndroid ApiとAndroid REST Apiの共存