Google drive SDK 2.0 throws error 400 Bad Request - android

I've been fighting with Google Drive API for Android for more than 50 hours now, and have not come one inch closer. From my understanding, there are 1001 ways to access Google drive (Google Docs API, REST & Google Drive SDK v2). I'm using Google Drive SDK v2. I want want to access Google Drive to upload jpeg files. Platform, Android 2.2+.
What I've tried:
Using the recently released SDK:
http://code.google.com/p/google-api-java-client/wiki/APIs#Drive_API
I've watched the Google I/O sesssion, but the most important part (how to create a Drive object using your Client ID & Client Secret) was left out:
https://developers.google.com/events/io/sessions/gooio2012/705/
I have created multiple keys on https://code.google.com/apis/console. The last one I've created (and tested with) was created using "Create another client ID..." -> "Installed Application" -> "Android". I've used the key in the ~/.android/debug.keystore.
I've also tried to create a key for an "Other" (instead of Android/iOS) installed app, but this gives me a Client ID and Client secret. It seems like the Drive object does not accept a client secret.
Where the code says "1234567890-abcdefghij123klmnop.apps.googleusercontent.com", I've tried to use both "API key" and the "Client ID", both gave the same error.
My code:
Account account = AccountManager.get(context).getAccountsByType(
"com.google")[0];
String token;
try {
token = GoogleAuthUtil.getToken(context, account.name, "oauth2:"
+ DriveScopes.DRIVE_FILE);
} catch (UserRecoverableAuthException e) {
context.startActivityForResult(e.getIntent(), ASK_PERMISSION);
return;
} catch (IOException e) {
return;
} catch (GoogleAuthException e) {
return;
}
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
Drive.Builder b = new Drive.Builder(httpTransport, jsonFactory, null);
final String tokenCopy = token;
b.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
public void initialize(JsonHttpRequest request) throws IOException {
DriveRequest driveRequest = (DriveRequest) request;
driveRequest.setPrettyPrint(true);
driveRequest
.setKey("1234567890-abcdefghij123klmnop.apps.googleusercontent.com");
driveRequest.setOauthToken(tokenCopy);
}
});
final Drive drive = b.build();
FileList files;
try {
files = drive.files().list().setQ("mimeType=text/plain").execute();
} catch (IOException e) {
e.printStackTrace(); // throws HTTP 400
}
The error I'm getting is:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 400 Bad Request
{
"code" : 400,
"errors" : [ {
"domain" : "global",
"location" : "q",
"locationType" : "parameter",
"message" : "Invalid Value",
"reason" : "invalid"
} ],
"message" : "Invalid Value"
}

As the error message suggests, your error is in the query parameter q. The correct syntax for your q parameter is
files = drive.files().list().setQ("mimeType='text/plain'").execute();
and not :
files = drive.files().list().setQ("mimeType=text/plain").execute();
Looking at your code, you are fully authenticated and your request is failing because of this syntax error.

Related

Youtube data api error in signed apk?

I'm trying to implement the functionality of add subscription to youtube channel from android app.
i already done with :
register app on developer console. -Done
package name and SHA-1 certificate fingerprint. -Done
Image
Google account account authentication with "https://www.googleapis.com/auth/youtube" . -Done
Note : functionality is working fine in debug mode.
Issue: when i create a signed apk for publishing app on play store then subscribe button not working throw different errors each time .
i.e With unrestricted api key :
W/System.err: {
"c" : 0,
"errors" : [ {
"domain" : "global",
"reason" : "required",
"message" : "Required parameter: part",
"locationType" : "parameter",
"location" : "part"
} ],
"code" : 400,
"message" : "Required parameter: part"
}
Here is the code that Im doing
// Initialize credentials and service object.
mCredential = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Arrays.asList(SCOPES)).setBackOff(new ExponentialBackOff());
HttpTransport transport = AndroidHttp.newCompatibleTransport();
JsonFactory jsonFactory = JacksonFactory.getDefaultInstance();
YouTube mService = new YouTube.Builder(transport, jsonFactory, mCredential)
.setApplicationName(getResources().getString(R.string.app_name))
.setYouTubeRequestInitializer(new YouTubeRequestInitializer(Config.KEY))
.build();
channelId = myListModel.getYoutubeChannelId();
// Create a resourceId that identifies the channel ID.
ResourceId resourceId = new ResourceId();
resourceId.setChannelId(channelId);
resourceId.setKind("youtube#channel");
// Create a snippet that contains the resourceId.
SubscriptionSnippet snippet = new SubscriptionSnippet();
snippet.setResourceId(resourceId);
// Create a request to add the subscription and send the request.
// The request identifies subscription metadata to insert as well
// as information that the API server should return in its response.
Subscription subscription = new Subscription();
subscription.setSnippet(snippet);
YouTube.Subscriptions.Insert subscriptionInsert = mService.subscriptions().insert("snippet,contentDetails", subscription);
try {
Subscription returnedSubscription = subscriptionInsert.execute();
// Print information from the API response.
System.out.println("\n================== Returned Subscription ==================\n");
System.out.println(" - Id: " + returnedSubscription.getId());
System.out.println(" - Title: " + returnedSubscription.getSnippet().getTitle());
addSubscriber(myListModel);
} catch (UserRecoverableAuthIOException mLastError) {
startActivityForResult(mLastError.getIntent(), REQUEST_AUTHORIZATION);
}
} catch (GoogleJsonResponseException e) {
System.err.println("GoogleJsonResponseException code: " + e.getDetails().getCode() + " : "
+ e.getDetails().getMessage());
e.printStackTrace();
} catch (IOException e) {
System.err.println("IOException: " + e.getMessage());
e.printStackTrace();
} catch (Throwable t) {
System.err.println("Throwable: " + t.getMessage());
t.printStackTrace();
}

Android expiry time of In-app subscription item in android

I am trying to get expiry time or status of subscription to ensure if user is paying regularly for my item or not . When i query using
Purchase monthlySubscription = inv.getPurchase("itemName");
or
ArrayList<String> ownedSkus = ownedItems.getStringArrayList("INAPP_PURCHASE_ITEM_LIST");
It returns following data
{
"packageName":"com.abcPackage",
"productId":"auto1week",
"purchaseTime":1453369299644,
"purchaseState":0,
"developerPayload":"PAY_LOAD",
"purchaseToken":"TOKEN",
"autoRenewing":true
}
The problem is , purchaseTime remains same after several weeks which is supposed to be change after every purchase.
I tried google Play developers API
https://developers.google.com/android-publisher/#subscriptions
but i am having a hard time implementing it on my android device .
I will be grateful if someone can guide me step by step process to get this data on android device.
https://developers.google.com/android-publisher/api-ref/purchases/subscriptions
Any help in this regard will be highly appreciated.
Not sure if this will help, but here is my server side code (in java) that connects to the developer API and returns the expiration of the subscription.
I created a Service Account in the Google Developer Console, and followed the somewhat obtuse instructions to create a key file in src/resources/keys.json. APPLICATION_NAME is the package name of my app, and PRODUCT_ID is the subscription ID from the Google PLAY developer console.
Sorry it's not really 'step by step' as you asked for, but I also am doing verification on the server side instead of on the client. I suppose on the client you could do some sort of soft-verification by checking purchaseState == 0 (1=cancelled, 2=refunded), and autoRenewing==true. You may get stuck there if they cancel though, since you are still supposed to provide service through the duration of the subscription.
public static Long doSomeWork(String token){
log.debug("Google Validation: Doing some work:" + token);
try{
// Creating new Trusted Transport
HttpTransport httpTransport = GoogleNetHttpTransport.newTrustedTransport();
JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance();
// Getting Auth Creds
Credential cred = getAuthCredential();
// Building Android Publisher API call
AndroidPublisher ap = new AndroidPublisher.Builder(httpTransport, JSON_FACTORY, cred)
.setApplicationName(APPLICATION_NAME).build();
// Get Subscription
AndroidPublisher.Purchases.Subscriptions.Get get = ap.purchases().subscriptions().get(
APPLICATION_NAME,
PRODUCT_ID,
token);
SubscriptionPurchase subscription = get.execute();
log.debug(subscription.toPrettyString());
log.debug("DONE (not null)");
return subscription.getExpiryTimeMillis();
} catch(IOException ex) {
ex.printStackTrace();
} catch (GeneralSecurityException ex2) {
ex2.printStackTrace();
}
log.debug("DONE (failure) (0)");
return 0L;
}
private static Credential getAuthCredential(){
log.debug("getAuthCredential");
try{
//Read the credentials from the keys file. This file is obtained from the
// Google Developer Console (not the Play Developer Console
InputStream is = GoogleReceiptValidation.class.getClassLoader().getResourceAsStream("keys.json");
String str = IOUtils.toString(is);
is.close();
JSONObject obj = new JSONObject(str);
InputStream stream = new ByteArrayInputStream(obj.toString().getBytes());
//This is apparently "beta functionality".
GoogleCredential creds = GoogleCredential.fromStream(stream);
creds = creds.createScoped(Collections.singleton(AndroidPublisherScopes.ANDROIDPUBLISHER));
return creds;
} catch (IOException ex){
ex.printStackTrace();
} catch (JSONException ex2){
ex2.printStackTrace();
}
log.debug("No Creds found - returning null");
return null;
}

Create GoogleCredential by Access Token

I want to design an Android file viewer for Google Drive.
At first, I implemented the app by using of the Google Android API, as follows,
private void retrieveNextPage(){
if(mHasMore == false)
return;
Query query = new Query.Builder().setPageToken(mNextPageToken).build();
com.google.android.gms.drive.Drive.DriveApi.query(getGoogleApiClient(), query).setResultCallback(metadataBufferResultResultCallback);
}
However, the Android Drive API only allows the app to view and fetch the files that created by itself. I cannot access other files on the drive through the app.
Therefore, I turned to another option, directly manipulate the Java Drive API.
According to the example on developer guide for Java,
https://developers.google.com/drive/web/quickstart/quickstart-java
The users have to manually copy and paste the "Authorization Code" between the browser and app, which is not a practical way to acquire the Access Token in Android.
To come out a new way, I used the GoogleAuthUtil in Android API to acquire the Access Token, coincided with the GoogleCredential and Drive in Java API to fetch the file list, as follows,
private static List<File> retrieveFiles(Drive service) throws IOException{
List<File> result = new ArrayList<File>();
Files.List request = service.files().list();
do {
try{
FileList fileList = request.execute();
result.addAll(fileList.getItems());
request.setPageToken(fileList.getNextPageToken());
}catch (IOException e){
Log.d(dbgT + "JavaRetrieveFiles", "Retrieved Failed");
request.setPageToken(null);
}
}while (request.getPageToken() != null && request.getPageToken().length() > 0);
return result;
}
private class RetrieveTokenTask extends AsyncTask<String, Void, String>{
#Override
protected String doInBackground(String... params){
String accountName = params[0];
String scopes = "oauth2:" + "https://www.googleapis.com/auth/drive";
String token = null;
try{
token = GoogleAuthUtil.getToken(getApplicationContext(), accountName, scopes);
}
catch (IOException e){
Log.e(excpTAG, "IO Exception: " + e.getMessage());
}
catch (UserRecoverableAuthException e){
startActivityForResult(e.getIntent(), REQ_SIGN_IN_REQUIRED);
}
catch (GoogleAuthException e)
{
Log.e(excpTAG, "GoogleAuthException: " + e.getMessage());
}
return token;
}
#Override
protected void onPostExecute(String s){
super.onPostExecute(s);
//Get Access Token
Log.d( dbgT + "Token", s);
EditText tokenText = (EditText) findViewById(R.id.tokenText);
tokenText.setText(s);
EditText fileNameText = (EditText) findViewById(R.id.editTextMeta);
GoogleCredential credential = new GoogleCredential().setAccessToken(s);
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new JacksonFactory();
Drive service = new Drive.Builder(httpTransport, jsonFactory, null).setHttpRequestInitializer(credential).build();
List<File> fileList;
try{
fileList = retrieveFiles(service);
for(int i=0; i< fileList.size(); i++)
fileNameText.append(fileList.get(i).getTitle());
}catch(IOException e){
Log.d(dbgT + "RetrieveFileList", "IO Exception" );
}
}
}
Unfortunately, the app always crashes by the causing of NetworkOnMainThreadException when request.execute() in retrieveFiles is invoked.
I checked my access token s, it is usually in form of ya29.xxx...etc., and it can also be passed to my other .NET program for retrieving files from Google Drive. Therefore I can certain the access token is correct.
So my question is, how to create a correct GoogleCredential by using of access token, instead of applying authorization code in setFromTokenResponse ?
Thanks in advance.
Many thanks for Andy's tips, this problem is simply caused by the network operations occurs on the main thread, which is a very basic newbie error.
The Drive in Google Drive SDK for Java, using network libraries without any background/thread worker, and now it is functional after I put the retrieveFiles() into background.
Applying the GoogleAuthUtil in Google Play Android SDK to acquire the access token, and followed by GoogleCredential+Drive in Java SDK that use the token to do the file operation in Google Drive.
This is a right way to avoid the scope restriction in Android SDK for Google Drive, allowing the developers to acquire the full permissive of accessing Google Drive.

Retrieve Google+ activities returns 403, "Access Not Configured" in Android, using GoogleIO13 sample app code

I am tryng to use the Google IO 2013 code sample, specifically the SocialStreamFragment to display google plus post based on a hashtag search.
Steps done:
Integrated SocialStreamFragment.java class and associated clasees, etc, in my project.
In the Console API , I have enabled Google+ API for my project
In the Console API I generated a Simple API Access key for Android Apps, configuring my app's package and debug/prod keys as allowed app.
I copied the Simple Access Key as Config.API_KEY.
However the code below to get the Activities/Posts fails with IOException : 403, "Access Not Configured".
public List<Activity> loadInBackground() {
mIsLoading = true;
// Set up the HTTP transport and JSON factory
HttpTransport httpTransport = new NetHttpTransport();
JsonFactory jsonFactory = new GsonFactory();
// Set up the main Google+ class
Plus plus = new Plus.Builder(httpTransport, jsonFactory, null)
.setApplicationName(NetUtils.getUserAgent(getContext()))
.setGoogleClientRequestInitializer(
new CommonGoogleClientRequestInitializer(Config.API_KEY))
.build();
ActivityFeed activities = null;
try {
activities = plus.activities().search(mSearchString)
.setPageToken(mNextPageToken)
.setOrderBy("recent")
.setMaxResults(MAX_RESULTS_PER_REQUEST)
.setFields(PLUS_RESULT_FIELDS)
.execute();
mHasError = false;
mNextPageToken = activities.getNextPageToken();
} catch (IOException e) {
e.printStackTrace();
mHasError = true;
mNextPageToken = null;The
}
return (activities != null) ? activities.getItems() : null;
}
What am I missing or doing wrong?
My project has also in API Console configured Client IDs for installed applications. Can this be a problem?
Have you got the Google+ API enabled in the API console?

Google Drive Returning Error 400 or 403 to my Android App?

Here's my code for requesting the auth token...
AccountManager am = AccountManager.get(this);
Bundle options = new Bundle();
am.getAuthToken(
(am.getAccounts())[0], // My test device only has one account. I'll add a picker before releasing this app.
"oauth2:" + DriveScopes.DRIVE,
options,
true, // I've tried both true and false... doesn't seem to change anything?
new OnTokenAcquired(),
null); // I used to have a handler, but it absolutely never got called.
Here's the code that handles the token:
private class OnTokenAcquired implements AccountManagerCallback<Bundle> {
#Override
public void run(AccountManagerFuture<Bundle> result) {
String token = result.getResult().getString(AccountManager.KEY_AUTHTOKEN);
HttpTransport httpTransport = new NetHttpTransport();
JacksonFactory jsonFactory = new JacksonFactory();
Drive.Builder b = new Drive.Builder(httpTransport, jsonFactory, null);
b.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
#Override
public void initialize(JsonHttpRequest request) throws IOException {
DriveRequest driveRequest = (DriveRequest) request;
driveRequest.setPrettyPrint(true);
driveRequest.setKey("xxxxxxxxxxxx.apps.googleusercontent.com"); // I replaced the number with x's. I'll explain where I got the number from below.
driveRequest.setOauthToken(token);
}
});
final Drive drive = b.build();
final com.google.api.services.drive.model.File body = new com.google.api.services.drive.model.File();
body.setTitle("My document");
body.setDescription("A test document");
body.setMimeType("text/plain");
java.io.File fileContent = new java.io.File("document.txt");
final FileContent mediaContent = new FileContent("text/plain", fileContent);
new Thread(new Runnable() {
public void run() {
com.google.api.services.drive.model.File file;
try {
file = drive.files().insert(body, mediaContent).execute();
Log.i("Hi", "File ID: " + file.getId());
} catch (IOException e) {
Log.i("Hi", "The upload/insert was caught, which suggests it wasn't successful...");
e.printStackTrace();
AccountManager am = AccountManager.get(activity);
am.invalidateAuthToken(am.getAccounts()[0].type, null);
Bundle options = new Bundle();
am.getAuthToken(
(am.getAccounts())[0],
"oauth2:" + DriveScopes.DRIVE,
options,
true,
new OnTokenAcquired(),
null);
}
}
}).start();
Intent launch = (Intent)result.getResult().get(AccountManager.KEY_INTENT);
if (launch != null) {
Log.i("Hi", "Something came back as a KEY_INTENT");
startActivityForResult(launch, 3025);
return;
} else {
Log.i("Hi", "I checked, but there was nothing for KEY_INTENT.");
}
}
}
I have implemented onActivityResult and have it set up to log the requestCode and resultCode if it's ever invoked, but it never is. Were it to ever be invoked, it just fires off another token request if the requestCode is 3025 and the resultCode is RESULT_OK.
Here's how I got my clientID:
I went to Google APIs console or whatever it's called.
I made a new product.
I enabled and set up the Drive SDK (sort of... I'm not making a web app, so I just pointed the URL at a website I own.)
Under "API Access" I clicked "Create another client ID..." and set it up for an installed Android app. (The fingerprint may not be quite right? Would that cause the error?)
I copied the Client ID listed under Client ID for Installed Applications (not the one from Client ID for Drive SDK).
As it is, here's what I get from the log:
I checked, but there was nothing for KEY_INTENT // My log message.
The upload/insert was caught, which suggests it didn't work... // Another one of my log messages.
com.google.api.client.http.HttpResponseException: 400 Bad Request
{
"error": {
"errors": [
{
"domain": "usageLimits",
"reason": "keyInvalid",
"message": "Bad Request"
}
],
"code": 400,
"message": "Bad Request"
}
}
Followed by a more ordinary stack trace...
EDIT
Recently it stopped giving me 400 and instead started giving me 403 Forbidden, still with the domain of usageLimits, reason now being accessNotConfigured and message being Access Not Figured.
You are passing the Client ID you got from the APIs Console to driveRequest.setKey, however that method is used to set an API Key and not the Client ID. That's why your application is not correctly bound to the project from the APIs Console and your app is not allowed to use the API.
Once you get a valid OAuth token for the project identified by that Client ID, you set it by calling driveRequest.setOauthToken(String).
For more details about OAuth2 on Android check http://developer.android.com/training/id-auth/authenticate.html
The issue appears to have been that in APIs Console I hadn't turned on both DRIVE SDK and DRIVE API under services. They're separate, and one doesn't automatically turn the other on.

Categories

Resources