1

I'm having trouble with Google Drive API on my Android app. On my device, a Nexus 4 with Android 5.1.1 it's working fine. On my user's devices, the consent screen I had configured is not showing, instead, just a list selection dialog with the user's e-mail is showing, but it doesn't do anything.

I saw this issue on tree other devices with Android 5.0.1.

I configured the OAuth 2.0 credentials with my production SHA1 key and the consent screen. Like I said, it's working fine on my device.

I'm using the following parameters to compile my app:

  • minSdkVersion=15
  • targeSdKVerion=22
  • compileSdkVersion=22
  • Build Tools: 22.0.1
  • Platform Tools: 23rc3

And these google libs:

  • com.android.support:support-v4:22.2.0
  • com.android.support:appcompat-v7:22.2.0
  • com.android.support:design:22.2.0
  • com.google.android.gms:play-services-ads:7.5.0
  • com.google.android.gms:play-services-drive:7.5.0

Thank you!

Here is how my code looks now:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_CODE_RESOLUTION) {
        if (resultCode == RESULT_OK) {
            // Make sure the app is not already connected or attempting to connect.
            if (!mBackupAsyncTask.getGoogleApiClient().isConnecting() &&
                    !mBackupAsyncTask.getGoogleApiClient().isConnected()) {
                // Connect and execute the task.
                mBackupAsyncTask.getGoogleApiClient().connect();
            }
        }
    }
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
    Log.i(LOG_TAG, "GoogleApiClient connection failed: " + connectionResult.toString());

    if (!connectionResult.hasResolution()) {
        Log.i(LOG_TAG, "Error with no resolution.");
        // Show the localized error dialog.
        GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();
    } else {
        // The failure has a resolution. Resolve it.
        // Called typically when the app is not yet authorized, and an authorization
        // dialog is displayed to the user.
        try {
            connectionResult.startResolutionForResult(this, REQUEST_CODE_RESOLUTION);
        } catch (IntentSender.SendIntentException e) {
            Log.e(LOG_TAG, "Exception while starting resolution activity.", e);
        }
    }
}

public class DriveBackupAsyncTask extends ApiClientAsyncTask<Object, Void, Metadata> {

    public DriveBackupAsyncTask(Context context) {
        super(context);
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mProgressBar.setIndeterminate(true);
        mEfetuarBackupButton.setEnabled(false);
        mRestaurarBackupButton.setEnabled(false);
    }

    @Override
    protected Metadata doInBackgroundConnected(Object... params) {
        if (params[0].equals(TASK_BACKUP)) {
            DriveFile backupFile = DriveBackupHelper.createBackup(
                    getGoogleApiClient(),
                    APP_FOLDER,
                    BACKUP_FILE,
                    getDbFile());

            if (backupFile != null)
                return backupFile.getMetadata(getGoogleApiClient()).await().getMetadata();
        }

        if (params[0].equals(TASK_RESTAURAR)) {
            DriveFile backupFile = DriveBackupHelper.getBackupFile(
                    getGoogleApiClient(),
                    APP_FOLDER,
                    BACKUP_FILE);

            if (backupFile == null)
                return null;

            if (DriveBackupHelper.restoreBackup(getGoogleApiClient(), getDbFile(), backupFile))
                return backupFile.getMetadata(getGoogleApiClient()).await().getMetadata();
        }

        return null;
    }

    @Override
    protected void onPostExecute(Metadata result) {
        super.onPostExecute(result);
        mProgressBar.setIndeterminate(false);
        mEfetuarBackupButton.setEnabled(true);
        mRestaurarBackupButton.setEnabled(true);

        if (result == null) {
            showError(getString(R.string.bkp_error));
        } else {
            showMessage(getString(R.string.bkp_success));
        }
    }
}
Kara
  • 6,115
  • 16
  • 50
  • 57
  • Is the dialog asking the user to select an account? If there are multiple Google accounts on the phone and you have not built your GoogleApiClient with `useDefaultAccount()`, a dialog is shown asking the user to select an account. You say the dialog "doesn't do anything". Can you provide more details? – Bob Snyder Jun 29 '15 at 21:49
  • Yes, is the dialog asking user to select an account. When user selects an account, nothing happens. I didn't build my client with useDefaultAccount option. – Vinicius Avellar Jun 29 '15 at 23:45
  • 1
    Actually this dialog appears even if user has just one account. – Vinicius Avellar Jun 29 '15 at 23:48

1 Answers1

1

This explanation assumes that your code is based on the Google API Guide for handling connection failures and is structured similarly to the example shown below (this is copied from the Guide):

@Override
public void onConnectionFailed(ConnectionResult result) {
    if (mResolvingError) {
        // Already attempting to resolve an error.
        return;
    } else if (result.hasResolution()) {
        try {
            mResolvingError = true;
            // This statement causing posting of the account picker dialog
            // or other UI to resolve the connection problem.
            result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
        } catch (SendIntentException e) {
            // There was an error with the resolution intent. Try again.
            mGoogleApiClient.connect();
        }
    } else {
        // Show dialog using GooglePlayServicesUtil.getErrorDialog()
        showErrorDialog(result.getErrorCode());
        mResolvingError = true;
    }
}

The dialog you are seeing is created when the user needs to select an account. It is generated by this statement in onConnectionFailed():

result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);

Where REQUEST_RESOLVE_ERROR is an integer constant you define. When the user selects an account in the dialog, your activity receives the result with request code equal to REQUEST_RESOLVE_ERROR. Your activity must have a method to handle the result similar to this (also copied from the Guide):

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (requestCode == REQUEST_RESOLVE_ERROR) {
        mResolvingError = false;
        if (resultCode == RESULT_OK) {
            // Make sure the app is not already connected or attempting to connect
            if (!mGoogleApiClient.isConnecting() &&
                    !mGoogleApiClient.isConnected()) {
                mGoogleApiClient.connect();
            }
        }
    }
}

When you say in your post that the dialog "doesn't do anything", it suggests that you do not have an onActivityResult() method or, if you do, the code there is not handling the result from the dialog correctly.

If this answer does not help you and you already have an onActivityResult(), please post it along with onConnectionFailed().

Bob Snyder
  • 37,759
  • 6
  • 111
  • 158
  • Ok, my bad. At first I was using async requests to access google drive, but then I changed to sync requests using this demo app: https://github.com/googledrive/android-demos/blob/master/src/com/google/android/gms/drive/sample/demo/SyncRequestsActivity.java I ripped off the onActivityResult from my app because I haven't seen the method on the demo, but I forgot to check the BaseDemoActivity. Now my app seems to work fine, I just don't know yet why it was working on my device. – Vinicius Avellar Jun 30 '15 at 12:52
  • I'm still learning about GoogleApi connections also. My experience has been that the first time an app tries to connect, the [AccountPicker](https://developers.google.com/android/reference/com/google/android/gms/common/AccountPicker) is displayed and then the consent dialog. If the user completes both of those and a connection is made, the Account manager saves the selected account as the default and any OAuth tokens. On subsequent connection requests, the saved values are used, for the convenience of the user (continued in next comment). – Bob Snyder Jun 30 '15 at 15:13
  • For development testing, it would be nice to be able to clear the saved values, to exercise all the code for connection failure resolution. I haven't found a way to do that. This [related issue](http://stackoverflow.com/questions/26457118/user-sign-out-clearing-the-default-google-account-does-not-cause-the-account-pi) discusses the difficulties others have had with this. – Bob Snyder Jun 30 '15 at 15:16
  • Yes, a way to clear the credentials would be nice. Every time I have to go to the drive options and remove my app from the list. – Vinicius Avellar Jun 30 '15 at 15:50
  • [This answer](http://stackoverflow.com/questions/31143973/how-to-clear-googleapiclient-default-account-and-credentials) provides another option. – Bob Snyder Jun 30 '15 at 17:53