10

The app was registered in the "Google API console" as an "installed application" - seems that this is the right setting for an Android app, no?

So I do have a Client-Id and no Secret-Id. To make it clear: It's no Web-app and no Google Drive-App - it's an Android App accessing other users data in the Google Drive cloud.

Within the app I fetch the account (works) and I do request a token (works). Now I want to connect to Google Drive with that token and the Client-Id. The result is a "401, invalid credential". What's wrong with this code?

public class ActivityMain extends Activity implements DialogInterface.OnClickListener {

    // https://developers.google.com/drive/scopes
    private static final String AUTH_TOKEN_TYPE = "oauth2:https://www.googleapis.com/auth/drive";

    // https://code.google.com/apis/console/
    private static final String CLIENT_ID = "999999999999999.apps.googleusercontent.com";

    private AccountManager accountManager;
    private Account[] accounts;
    private String authName;
    private String authToken;

    @Override
    public void onClick(final DialogInterface dialogInterface, final int item) {

        processAccountSelected(accounts[item]);
    }

    @Override
    public void onCreate(final Bundle bundle) {
        super.onCreate(bundle);

        setContentView(R.layout.activitymain);

        accountManager = AccountManager.get(this);
        accounts = accountManager.getAccountsByType("com.google");

        if (accounts == null || accounts.length == 0) {
            // TODO
        } else if (accounts.length == 1) {
            processAccountSelected(accounts[0]);
        } else if (accounts.length > 1) {
            showDialog(MyConstants.DIALOG_ACCOUNTCHOSER);
        }
    }

    @Override
    protected Dialog onCreateDialog(final int id) {
        switch (id) {
            case MyConstants.DIALOG_ACCOUNTCHOSER:
                AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(this);

                String[] names = new String[accounts.length];

                for (int i = 0; i < accounts.length; i++) {
                    names[i] = accounts[i].name;
                }

                alertDialogBuilder.setItems(names, this);
                alertDialogBuilder.setTitle("Select a Google account");
                return alertDialogBuilder.create();
        }

        return null;
    }

    private void processAccountSelected(final Account account) {
        if (account != null) {
            authName = account.name.toString();
            if (!Tools.isEmpty(authName)) {
                Toast.makeText(this, authName, Toast.LENGTH_LONG).show();

                accountManager.getAuthToken(account, AUTH_TOKEN_TYPE, null, this,
                        new AccountManagerCallback<Bundle>() {

                            public void run(final AccountManagerFuture<Bundle> future) {
                                try {
                                    authToken = future.getResult().getString(
                                            AccountManager.KEY_AUTHTOKEN);
                                    processTokenReceived();
                                } catch (OperationCanceledException exception) {
                                    // TODO
                                } catch (Exception exception) {
                                    Log.d(this.getClass().getName(), exception.getMessage());
                                }
                            }
                        }, null);
            }
        }
    }

    private void processListFiles(final Drive drive) {
        List<File> result = new ArrayList<File>();
        Files.List request = null;
        try {
            request = drive.files().list();
        } catch (IOException exception) {
        }

        do {
            try {
                FileList files = request.execute();

                result.addAll(files.getItems());
                request.setPageToken(files.getNextPageToken());
            } catch (IOException exception) {
                // --> 401 invalid credentials
            }
        } while (request.getPageToken() != null && request.getPageToken().length() > 0);
    }

    private void processTokenReceived() {
        if (!Tools.isEmpty(authToken)) {
            final HttpTransport transport = AndroidHttp.newCompatibleTransport();
            final JsonFactory jsonFactory = new GsonFactory();
            GoogleCredential credential = new GoogleCredential();
            credential.setAccessToken(authToken);
            Drive drive = new Drive.Builder(transport, jsonFactory, credential)
                    .setApplicationName(getString(R.string.txt_appname))
                    .setJsonHttpRequestInitializer(new GoogleKeyInitializer(CLIENT_ID))
                    .build();

            if (drive != null) {
                processListFiles(drive);
            }
        }
    }
}

I have to say that this is a full load of mess. There are so many pages in the web showing parts only and there are so many pages using deprecated, missing or different methods to do the same. There are, in my opinion, not two pages showing the same way to get data from Google Drive from within an Android app.

Any help is highly appreciated.

EDIT: I could solve it myself. It was a combination of different changes:

  • Had to set android:minSdkVersion="11" as a requirement
  • Had to use this current libraries: google-api-client-1.11.0-beta.jar, google-api-client-android-1.11.0-beta.jar, google-api-services-drive-v2-rev9-1.8.0-beta.jar, google-http-client-1.11.0-beta.jar, google-http-client-android-1.11.0-beta.jar, google-http-client-gson-1.11.0-beta.jar, google-http-client-jackson2-1.11.0-beta.jar, google-oauth-client-1.11.0-beta.jar, gson-2.1.jar, guava-11.0.1.jar, jackson-core-2.0.5.jar, jsr305-1.3.9.jar

This is the current part to get the Drive Object:

    GoogleCredential credential = new GoogleCredential();
    credential.setAccessToken(authToken);

    HttpTransport transport = AndroidHttp.newCompatibleTransport();

    JsonFactory jsonFactory = new AndroidJsonFactory();

    drive = new Drive.Builder(transport, jsonFactory, credential)
            .setApplicationName(getString(R.string.txt_appname))
            .setJsonHttpRequestInitializer(
                    new GoogleKeyInitializer(APIKEY_SIMPLE))
            .build();
    if (drive != null) {
    }
Harald Wilhelm
  • 6,656
  • 11
  • 67
  • 85

4 Answers4

3

Yeah, the documentation is quite hard to catch on.

Just change

new GoogleKeyInitializer(CLIENT_ID)

to

new GoogleKeyInitializer(SIMPLE_API_ACCESS_KEY)

and it should work.

You can find your SIMPLE_API_ACCESS_KEY in Google's APIs Console under the Simple API Access section of the API Access page (the API key). If this section is not available, you have to first activate Drive API access on the Services page.

Jan Gerlinger
  • 7,361
  • 1
  • 44
  • 52
  • Sorry, didn't work neither. Same error: 401, invalid credential. The simple API key section mentions "...when you do not need to access user data..." but this is true for my Android app. I don't get it. Is there nobody who did successfully create an Android app that connects to Google Drive data on behalf of a user who gave permission to do so thru AccountManager and Google Credentials? – Harald Wilhelm Sep 13 '12 at 13:40
  • Well the only difference between your code and the [Tasks API example](https://code.google.com/p/google-api-java-client/source/browse/tasks-android-sample/src/main/java/com/google/api/services/samples/tasks/android/TasksSample.java?repo=samples) I see then, is that they're calling `credential.setAccessToken(authToken)` *after* building the `Tasks` instance. So maybe exchanging those 2 lines does the trick? – Jan Gerlinger Sep 13 '12 at 14:48
  • No luck. Seems that there's something broken. Thanks anyway. – Harald Wilhelm Sep 13 '12 at 15:19
  • 1
    @HaraldWilhelm I'd like to direct you to my very extensive answer, including how to download files without the 401 error (I've updated it to include code on downloading at the end of the Android Code section of the answer), over here: http://stackoverflow.com/questions/12164024/android-open-and-save-files-to-from-google-drive-sdk – ArtOfWarfare Sep 26 '12 at 16:49
1

There are 2 issues with the snippet you included.

  1. You must invalidate the old authToken before fetching a new authToken from the AccountManager.

    AccountManager.get(activity).invalidateAuthToken("com.google", authToken);
    accountManager.getAuthToken(...);
    
  2. The call to setJsonHttpRequestInitializer must use the Simple API Key declared in the APIs Console for your project.

    • It can be obtained by visiting the APIs Console.
    • Click API Access on the left menu.
    • Look for Simple API Access, and copy the API key.
    • Set the API key when building the Drive object.

      .setJsonHttpRequestInitializer(new GoogleKeyInitializer(KEY))

There is a small sample demonstrating the token invalidation here: http://chiarg.com/?p=429

Chirag Shah
  • 3,654
  • 22
  • 29
  • Have you managed to make an Android app that can download files from Google Drive? If so, would you mind sharing the code that does it? To the best of my knowledge, I have the most complete Drive app not from Google that works on Android, and I can't get file downloading to work (I always get a 401 error... somehow I have permission to delete files entirely, or completely change their contents, but not download the current contents?) My code is here: http://stackoverflow.com/questions/12164024/android-open-and-save-files-to-from-google-drive-sdk/12321331#12321331 – ArtOfWarfare Sep 18 '12 at 15:15
0

I tried all this and finally found this code to work with the google Calendar api. I used to get a 401 every time i used GoogleCredentials and passed it to the build.

HttpTransport transport = AndroidHttp.newCompatibleTransport();;
    JsonFactory jsonFactory = new GsonFactory();
    HttpRequestInitializer httpRequestInitializer;

    Log.i("Reached calendar builder", "Reached calendar builder"+accessToken);

    //GoogleCredential credential = new GoogleCredential();

     httpRequestInitializer = new HttpRequestInitializer(){
        public void initialize(HttpRequest request) throws IOException
        {
                request.getHeaders().setAuthorization(GoogleHeaders.getGoogleLoginValue(accessToken));
        }
    };

    Calendar service = new Calendar.Builder(transport, jsonFactory, httpRequestInitializer)
    .setApplicationName("Meetrbus/1.0")
    .setJsonHttpRequestInitializer(new GoogleKeyInitializer("API_KEY"))
    .build();


}
SKen
  • 612
  • 1
  • 10
  • 20
  • Thanks for your answer. It didn't work. I tried your exact code with the Simple API key -> 401. My app is an "installed application" with a Client-Id that was taken from the API Console. I doubt that a Simple API Key works in combination with an "installed application". – Harald Wilhelm Sep 15 '12 at 15:30
0

I gave an extensive answer on using Google Drive on Android over here:

Android Open and Save files to/from Google Drive SDK

Using the methods I lay out in that answer, I can confirm that you're able to do the following:

  • Set and Access Meta-data
  • Upload Files
  • Download Files
  • Move files between Google Drive directories
  • Delete files off of the drive entirely
Community
  • 1
  • 1
ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193