5

I'm using Google Drive Android API (as part of Google Play Services) to upload files to cloud.

To connect client I'm using following code (simplified):

apiClient = new GoogleApiClient.Builder(context)
            .addApi(Drive.API)
            .setAccountName(preferences.getString("GOOGLE_DRIVE_ACCOUNT", null))
            .build();

ConnectionResult connectionResult = apiClient.blockingConnect(SERVICES_CONNECTION_TIMEOUT_SEC, TimeUnit.SECONDS);
if (!connectionResult.isSuccess()) {
    throw new ApiConnectionException(); //our own exception
}

To upload file I'm using something following code (simplified):

DriveApi.ContentsResult result = Drive.DriveApi.newContents(apiClient).await();
if (!result.getStatus().isSuccess()) {
    /* ... code for error handling ... */
    return;
}

OutputStream output = result.getContents().getOutputStream();
/* ... writing to output ... */

//create actual file on Google Drive
DriveFolder.DriveFileResult driveFileResult = Drive.DriveApi
            .getFolder(apiClient, folderId)
            .createFile(apiClient, metadataChangeSet, result.getContents())
            .await();

Everything works as expected except for one particular user case. When user removes our app from "Connected apps" (using Google Settings application) this code still returns successful results for all invocations. Although file is never uploaded to Google Drive.

Connection to Google Play Services also succeeds.

Is it a bug of API or it can be somehow detected that user disconnected the application?

Dmitry Zaytsev
  • 23,650
  • 14
  • 92
  • 146

3 Answers3

0

I don't know the API inside/out, however this page may help https://support.google.com/drive/answer/2523073?hl=en. I would double check the accounts.google.com page and verify that all permissions are being removed. This won't solve the api behaviour but at least you can verify the permissions.

SoundsDangerous
  • 708
  • 6
  • 13
0

Aren't you getting a UserRecoverableAuthIOException? Because you should. Any try to read/upload to drive where a user disconnected the app should return this Exception. You are probably catching general Exceptions and missing this. Try debugging to see if you aren't getting this exception.

If you are, all you have to do is re-request

        catch (UserRecoverableAuthIOException e) {
            startActivityForResult(e.getIntent(), COMPLETE_AUTHORIZATION_REQUEST_CODE);
        }

And then deal with the response like this:

case COMPLETE_AUTHORIZATION_REQUEST_CODE:
        if (resultCode == RESULT_OK) {
            // App is authorized, you can go back to sending the API request
        } else {
            // User denied access, show him the account chooser again
        }
        break;
    }
Dpedrinha
  • 3,741
  • 3
  • 38
  • 57
  • As you can see from my code, I'm not catching `Exception`. Although this case would make sense and I would expect such behavior as you described. Unfortunately it is not working like that currently. – Dmitry Zaytsev Jun 21 '14 at 09:15
  • Aren't you suppressing Exceptions or throwing it up in the hierarchy? Check if your class isn't throwing up Exceptions (and catching it somewhere else) or if you are not suppressing them. Because this code should require try/catch blocks. – Dpedrinha Jun 24 '14 at 20:44
  • As I said, I did checked behavior (with both log and debugger). Results **are** there and they, for some reason, successful. Exceptions are not thrown. – Dmitry Zaytsev Jun 24 '14 at 20:47
-1

For creating a file try sending a IntentSender which according to this

By giving a IntentSender to another application, you are granting it the right to perform the operation you have specified as if the other application was yourself (with the same permissions and identity). Looks like a Pending Intent. You can create a file using

ResultCallback<ContentsResult> onContentsCallback =
                    new ResultCallback<ContentsResult>() {
                @Override
                public void onResult(ContentsResult result) {
                    // TODO: error handling in case of failure
                    MetadataChangeSet metadataChangeSet = new MetadataChangeSet.Builder()
                            .setMimeType(MIME_TYPE_TEXT).build();
                    IntentSender createIntentSender = Drive.DriveApi
                            .newCreateFileActivityBuilder()
                            .setInitialMetadata(metadataChangeSet)
                            .setInitialContents(result.getContents())
                            .build(mGoogleApiClient);
                    try {
                        startIntentSenderForResult(createIntentSender, REQUEST_CODE_CREATOR, null,
                                0, 0, 0);
                    } catch (SendIntentException e) {
                        Log.w(TAG, "Unable to send intent", e);
                    }
                }
            };

In here

`startIntentSenderForResult (IntentSender intent, int requestCode, Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)`

If requestCode >= 0, this code will be returned in onActivityResult() when the activity exits. in your onActivityResult() you can

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
        //REQUEST_CODE_CREATOR == 1
        case REQUEST_CODE_CREATOR:
            if (resultCode == RESULT_OK) {
                DriveId driveId = (DriveId) data.getParcelableExtra(
                        OpenFileActivityBuilder.EXTRA_RESPONSE_DRIVE_ID);
                showMessage("File created with ID: " + driveId);
            }
            finish();
            break;
        default:
            super.onActivityResult(requestCode, resultCode, data);
            break;
        }
    }

Try to get the apiClient like this

mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addApi(Drive.API).addScope(Drive.SCOPE_FILE)
                    .setAccountName(mAccountName).addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this).build();



  /**
     * Called when {@code mGoogleApiClient} is connected.
     */
    @Override
    public void onConnected(Bundle connectionHint) {
        Log.i(TAG, "GoogleApiClient connected");
    }

     /**
     * Called when {@code mGoogleApiClient} is disconnected.
     */
    @Override
    public void onConnectionSuspended(int cause) {
        Log.i(TAG, "GoogleApiClient connection suspended");
    }

    /**
     * Called when {@code mGoogleApiClient} is trying to connect but failed.
     * Handle {@code result.getResolution()} if there is a resolution is
     * available.
     */
    @Override
    public void onConnectionFailed(ConnectionResult result) {
        Log.i(TAG, "GoogleApiClient connection failed: " + result.toString());
        if (!result.hasResolution()) {
            GooglePlayServicesUtil.getErrorDialog(result.getErrorCode(), this, 0).show();
            return;
        }
        try {
            result.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
        } catch (SendIntentException e) {
            Log.e(TAG, "Exception while starting resolution activity", e);
        }
    }

You can get the mAccountName like this:

Account[] accounts = AccountManager.get(this).getAccountsByType("com.google");
            if (accounts.length == 0) {
                Log.d(TAG, "Must have a Google account installed");
                return;
            }
            mAccountName = accounts[0].name;

Hope this helps.

android_Muncher
  • 1,057
  • 7
  • 14
  • 1
    I'm not looking for "how to" tutorial. Moreover, I do not need to create a file via `IntentSender` - I need to do it within my process – Dmitry Zaytsev Jun 12 '14 at 10:51