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) {
}
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.
There are 2 issues with the snippet you included.
You must invalidate the old authToken before fetching a new
authToken
from the AccountManager.The call to
setJsonHttpRequestInitializer
must use the Simple API Key declared in the APIs Console for your project.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
Yeah, the documentation is quite hard to catch on.
Just change
to
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 (theAPI key
). If this section is not available, you have to first activate Drive API access on the Services page.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: