I am trying to get OAuth token from AccountManager for twitter account and using it to follow a twitter handle using Twitter4j. But I am getting no authenticating challenges found TwitterException.
Bellow is the code to get the token and follow the user:
try{
AccountManager accountManager = AccountManager.get(getActivity());
Account twitterAccount = accountManager.getAccountsByType(AccountType.TWITTER.getType())[0];
AccountManagerFuture<Bundle> tokenFuture = accountManager.getAuthToken(twitterAccount, "com.twitter.android.oauth.token", null, true, new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
try {
bundle = result.getResult();
Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (intent != null) {
// User input required
getActivity().startActivity(intent);
} else {
String token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
Log.i(TAG, "Token:" + token);
userToken = token;
}
} catch (Exception e) {
Log.e(TAG, "Error getting token", e);
}
}
}, null);
AccountManagerFuture<Bundle> secretFuture = accountManager.getAuthToken(twitterAccount, "com.twitter.android.oauth.token.secret", null, true, new AccountManagerCallback<Bundle>() {
public void run(AccountManagerFuture<Bundle> result) {
Bundle bundle;
try {
bundle = result.getResult();
Intent intent = (Intent) bundle.get(AccountManager.KEY_INTENT);
if (intent != null) {
// User input required
getActivity().startActivity(intent);
} else {
String secret = bundle.getString(AccountManager.KEY_AUTHTOKEN);
Log.i(TAG, "Secret Token:" + secret);
userSecret = secret;
}
} catch (Exception e) {
Log.e(TAG, "Error getting token", e);
}
}
}, null);
ConfigurationBuilder configurationBuilder = new ConfigurationBuilder();
configurationBuilder
.setOAuthConsumerKey(<Consumer-key>);
configurationBuilder
.setOAuthConsumerSecret(<Consumer-Secret>);
configurationBuilder.setOAuthAccessToken(userToken);
configurationBuilder.setOAuthAccessTokenSecret(userSecret);
Twitter twitter = new TwitterFactory(configurationBuilder.build()).getInstance();
User user = twitter.createFriendship(param.getUserName());
Toast.makeText(followListActivity,"Followed user #"+user.getScreenName()+" :)",Toast.LENGTH_SHORT);
} catch (TwitterException e) {
e.printStackTrace();
}
Getting this exception:
TwitterException{exceptionCode=[ec1fe56d-1e3b4ac5 3ea58453-a92e09a2], statusCode=-1, message=null, code=-1, retryAfter=-1, rateLimitStatus=null, version=4.0.2}
at twitter4j.HttpClientImpl.handleRequest(HttpClientImpl.java:178)
at twitter4j.HttpClientBase.request(HttpClientBase.java:53)
at twitter4j.HttpClientBase.get(HttpClientBase.java:71)
at twitter4j.TwitterBaseImpl.fillInIDAndScreenName(TwitterBaseImpl.java:128)
at twitter4j.TwitterImpl.verifyCredentials(TwitterImpl.java:545)
at asynctask.FollowTwitterUserAsyncTask.doInBackground(FollowTwitterUserAsyncTask.java:113)
at asynctask.FollowTwitterUserAsyncTask.doInBackground(FollowTwitterUserAsyncTask.java:33)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.io.IOException: No authentication challenges found
at libcore.net.http.HttpURLConnectionImpl.getAuthorizationCredentials(HttpURLConnectionImpl.java:438)
at libcore.net.http.HttpURLConnectionImpl.processAuthHeader(HttpURLConnectionImpl.java:418)
at libcore.net.http.HttpURLConnectionImpl.processResponseHeaders(HttpURLConnectionImpl.java:367)
at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:301)
at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:497)
at libcore.net.http.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:134)
at twitter4j.HttpResponseImpl.<init>(HttpResponseImpl.java:35)
at twitter4j.HttpClientImpl.handleRequest(HttpClientImpl.java:142)
Please point out what I am doing wrong here.
You don’t do anything wrong at all, that’s an issue with the library. In Android, a HTTP response code of 401 causes an IOException be thrown. The library implementor checks for the message string of the IOException to catch it, however your message string is slightly different so the exception handling doesn’t execute. Unluckily, the issue tracker is not enabled for that GitHub project, but you may try to contact the project owner, maybe he will fix it for you. Otherwise you will have to modify the library code and build it yourself.
Related
I use android AccountManager to get authToken like this:
private void getGoogleAccountName(){
AccountManager.get(activity.getApplicationContext())
.getAuthTokenByFeatures("com.google", "oauth2:https://gdata.youtube.com", null, activity, null, null, this, null);
}
// I implement AccountManagerCallback<Bundle> in this class
#Override
public void run(AccountManagerFuture<Bundle> future) {
boolean hasAccount = checkGoogleAccount(future);
if (hasAccount) {
getYoutubeVideoByLib("");
}
}
private boolean checkGoogleAccount(AccountManagerFuture<Bundle> future){
try {
Bundle bundle = future.getResult();
accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN);
} catch (Exception e) {
return false;
}
return true;
}
then I use google apis with authToken trying to get user's playlists
private void getYoutubeVideoByLib(String pageToken){
YouTube youtube = new YouTube.Builder(
new NetHttpTransport(),
new JacksonFactory(),
new HttpRequestInitializer() {
#Override
public void initialize(HttpRequest request) throws IOException {
}
}).setApplicationName(activity.getString(R.string.app_name)).build();
YouTube.Playlists.List query = null;
try{
query = youtube.playlists().list("snippet");
query.setOauthToken(authToken);
query.setKey("YOUTBE_API_KEY");
query.setMine(true);
if(!TextUtils.isEmpty(pageToken)) {
query.setPageToken(pageToken);
}
PlaylistListResponse response = query.execute();
...
} catch(IOException e) {
return;
}
}
but I found out some google account got GoogleJsonResponseException in query.execute();
com.google.api.client.googleapis.json.GoogleJsonResponseException: 401 Unauthorized
{
"code" : 401,
"errors" : [ {
"domain" : "global",
"location" : "Authorization",
"locationType" : "header",
"message" : "Invalid Credentials",
"reason" : "authError"
} ],
"message" : "Invalid Credentials"
}
the weird thing is that, some accounts work fine before but after these users got this exception, they can't get their playlists anymore.
Does anyone meet the same problem?
===================================================================
I solved it myself. I made a big mistake...
In google developer console, I set API KEY but not OAuth 2.0 client ID.
After setting both and change code below, it work fine now.
String[] SCOPES = {YouTubeScopes.YOUTUBE_READONLY};
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(getApplicationContext(), Arrays.asList(SCOPES));
credential.setSelectedAccountName(accountName);
YouTube youtube = new YouTube.Builder(transport, jsonFactory, credential).setApplicationName(getString(R.string.app_name)).build();
My app allows users to login with Google Plus, and gets their name, and email address. I am tried to access the token.
Code to access the token:
Toast.makeText(this, "User is connected!", Toast.LENGTH_LONG).show();
GooglePlayServicesUtil.isGooglePlayServicesAvailable(getApplicationContext());
AccountManager am = AccountManager.get(this);
final Account[] accounts = am.getAccountsByType(GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE);
AsyncTask<Void, Void, String> task2 = new AsyncTask<Void, Void, String>() {
public static final int REQUEST_CODE_TOKEN_AUTH = 100;
#Override
protected String doInBackground(Void... params) {
String mScope="audience:server:client_id:899555500747-38rpnq51of946grhdvofck7r8u5p09cd.apps.googleusercontent.com:api_scope:https://www.googleapis.com/auth/plus.login";
// Get the token for the current user
String token = null;
try {
token = GoogleAuthUtil.getToken(getApplicationContext(), Plus.AccountApi.getAccountName(mGoogleApiClient), mScope);
Log.i("G token", token);
} catch (IOException transientEx) {
// Network or server error, try later
Log.e(TAG, transientEx.toString());
} catch (UserRecoverableAuthException e) {
// Recover (with e.getIntent())
Log.e(TAG, e.toString());
Intent recover = e.getIntent();
startActivityForResult(recover, REQUEST_CODE_TOKEN_AUTH );
} catch (GoogleAuthException authEx) {
// The call is not ever expected to succeed
// assuming you have already verified that
// Google Play services is installed.
Log.e(TAG, authEx.toString());
}
return token;
}
#Override
protected void onPostExecute(String token) {
Log.i(TAG, "Access token retrieved:" + token);
}
};
task2.execute();
Error:
01-27 23:42:14.877 30994-31262/com.unicloud.mittal E/loginWithGooglePlus﹕ com.google.android.gms.auth.GoogleAuthException: Unknown
01-27 23:42:14.877 30994-30994/com.unicloud.mittal I/loginWithGooglePlus﹕ Access token retrieved:null
I have tried various solutions which I could find on stackoverflow. At the moment, I am using Client ID from "Service Account" from dev console, I have also tried using it for "Client ID for Android Application", it still showed the same error.
Please let me know what am I doing wrong? Thanks.
I solved this question by replacing this line
String mScope="audience:server:client_id:899555500747-38rpnq51of946grhdvofck7r8u5p09cd.apps.googleusercontent.com:api_scope:https://www.googleapis.com/auth/plus.login";
with this
String mScope = "oauth2:https://www.googleapis.com/auth/plus.login";
I got the access token, and I was wondering if I am doing right. So I verified if the token was correct.
I tried to go to this address, I replaced accessToken with the token I retrieved.
https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=accessToken
It showed me this kind of output.
{
"issued_to": "xxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"audience": "xxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxxxxxxxxxxxx.apps.googleusercontent.com",
"user_id": "xxxxxxxxxxxxxxxxxxxxxxx",
"scope": "https://www.googleapis.com/auth/userinfo.profile https://gdata.youtube.com",
"expires_in": 3019,
"access_type": "online"
}
In the issued_to it showed me my Client ID for Android Application that means this token is issued to my client id. So I assume I am on the right track.
I am having a hard time trying to figure out what's going on. I have the typical app structure that spawns one OAuthActivity that takes care of getting a twitter token, then my main application activity uses that token for various twitter-related operations.
My OAuthActivity works. I get an auth token, and the Twitter web screen correctly shows my application name, etc.... Besides, inside that activity, I can send a tweet and it gets published. . This means the OAuthActivity works, the clock is in sync, the token is valid, etc...
But when this OAuthActivity finishes and returns to the calling activity, whenever I try to use that token (recreating it from the persisted key/secret), no matter for what, the operation always fails with a 401, complaining that AuthChallenge reported null... just like if I provided an empty token, but i haven't !!!
Please find attached the source of my OAuthActivity, and the source of how I initialize Twitter Objects in the main activity. Please tell me if you see something wrong.
PD - I have obviously checked that the token values I assign are the same I get !! Also tried different ways of instantiating Twitter, via properties, via builder, via sets .... and nothing changes :(
EDIT-> I found around I have to call "verifyCredentials()" on the new twitter object if I want to reuse a token, but .... no luck! (please find posted exception at the end)
EDIT-2> If I use on both the child activity and the parent
mTwitter=TwitterFactory.getSingleton()
then the twitter object works, but this is not really acceptable for me because It doesnt use persistance, and I would need to authorize the application everytime. Besides, only Twitter object is authorized, TwitterStream keeps throwing exceptions.
Cheers!
Source code of the parent activity, where I try to use an access token obtained in the child activity, listed below. Whatever I try to do with this token always gets the 401.
private void init_twitter(String tok, String sec) {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey(Conf.OAUTH_CONSUMER_KEY)
.setOAuthConsumerSecret(Conf.OAUTH_CONSUMER_SECRET)
.setOAuthAccessToken(tok)
.setOAuthAccessTokenSecret(sec);
TwitterFactory tf = new TwitterFactory(cb.build());
mTwitter=tf.getInstance();
/** This always fails, even though I call this routine with the
correct token & secret !!! See at the enf of message for an alternate
routine like this one that makes use of verifyCredentials and
also fails. */
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
try {
mTwitter.updateStatus("yello 2");
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}).start();
}
SOurce Code of the child OAuthActivity, it apparently works as I get an access token & am able to tweet:
import a lot;
public class TwitterLogin extends Activity {
private final String TAG = "TwitterLogin";
public final static String PREF_KEY_OAUTH_TOKEN="twitter.oauth.token", PREF_KEY_OAUTH_SECRET="twitter.oauth.secret", PREF_KEY_TWITTER_LOGIN="twitter.oauth.login";
private SharedPreferences mPreferences;
private Twitter twitter = new TwitterFactory().getInstance();
#Override
public void onCreate(Bundle savedInstanceState) {
Log.i(TAG, "Starting task to retrieve request token.");
this.mPreferences = PreferenceManager.getDefaultSharedPreferences(this);
super.onCreate(savedInstanceState);
getActionBar().setTitle("TWITTER AUTHENTICATION");
}
private void returnParent(boolean result) {
setResult(result?Activity.RESULT_OK:Activity.RESULT_CANCELED, null);
if (Conf.LOG_ON) Log.d(TAG, "TWITTER AUTH: END PROCESS , GLOBAL RESULT "+result);
/** THE FOLLOWING THING WORKS !!!!! IT SUCCESSFULLY TWEETS */
new Thread(new Runnable() {
#Override
public void run() {
// TODO Auto-generated method stub
try {
twitter.updateStatus("yello");
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}).start();
finish();
}
/**
* Uses TWITTER4J to get the Request URL. It gets something like
* AUTH URL TWITTER4J IS http://api.twitter.com/oauth/authorize?oauth_token=xxxxxxxxxxxxxxxxxxxxx
*
* #return The Request URL to open in webview and get the Verifier
*/
private String oauth_twitter4j_getRequestUrl() throws TwitterException {
twitter.setOAuthConsumer(Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
RequestToken tempToken = twitter.getOAuthRequestToken(Constants.OAUTH_CALLBACK_URL);
return tempToken.getAuthorizationURL();
}
#Override
protected void onResume() {
super.onResume();
WebView webview = new WebView(this);
webview.getSettings().setJavaScriptEnabled(true);
webview.setVisibility(View.VISIBLE);
setContentView(webview);
Log.i(TAG, "Retrieving request token from Google servers");
try {
StrictMode.ThreadPolicy policy = new StrictMode. ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy);
String authorizationUrl=oauth_twitter4j_getRequestUrl();
Log.d(TAG, "AUTH URL TWITTER4J IS "+authorizationUrl_t);
webview.setWebViewClient(new WebViewClient() {
#Override
public boolean shouldOverrideUrlLoading(WebView webView, String url) {
if (Conf.LOG_ON) Log.d(TAG,"WebView: "+url);
if (url != null && url.startsWith(Constants.OAUTH_CALLBACK_URL)) try {
System.out.println("TWEET TWEET TWEET");
retrieveAccessToken(url); //added this
webView.setVisibility(View.GONE); //added this
return true;
} catch (Exception e) {
e.printStackTrace();
returnParent(false);
return true;
} else return false;
}
private void saveAccessToken(AccessToken accessToken) {
// Shared Preferences
Editor e = mPreferences.edit();
// After getting access token, access token secret
// store them in application preferences
e.putString(PREF_KEY_OAUTH_TOKEN, accessToken.getToken());
e.putString(PREF_KEY_OAUTH_SECRET,accessToken.getTokenSecret());
e.putBoolean(PREF_KEY_TWITTER_LOGIN, true);
e.commit();
Log.e("Twitter OAuth Token", "> " + accessToken.getToken()+"-"+accessToken.getScreenName());
}
private void retrieveAccessToken(String url) throws Exception {
String requestToken = extractParamFromUrl(url,"oauth_token");
String verifier= extractParamFromUrl(url,"oauth_verifier");
if (Conf.LOG_ON) Log.d(TAG, "Tenemos ACCESS TOKEN y VERIFIER :"+requestToken+","+verifier+","+(new Date().toString()));
if (ONLY_TWITTER4J)
retrieveAccessToken_with4j(verifier);
else
retrieveAccessToken_signpost(verifier);
}
private void retrieveAccessToken_with4j(String verifier) throws TwitterException {
AccessToken a=twitter.getOAuthAccessToken(verifier);
saveAccessToken(a);
returnParent(true);
}
private String extractParamFromUrl(String url,String paramName) {
String queryString = url.substring(url.indexOf("?", 0)+1,url.length());
QueryStringParser queryStringParser = new QueryStringParser(queryString);
return queryStringParser.getQueryParamValue(paramName);
}
});
webview.loadUrl(authorizationUrl);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
Exception I get when calling VerifyCredentials with the token I'm sure is right:
Received authentication challenge is null
W/System.err(24915): Relevant discussions can be found on the Internet at:
W/System.err(24915): http://www.google.co.jp/search?q=6f0f59ca or
W/System.err(24915): http://www.google.co.jp/search?q=20d0f74e
W/System.err(24915): TwitterException{exceptionCode=[6f0f59ca-20d0f74e 1de2170b-f94dee38], statusCode=-1, message=null, code=-1, retryAfter=-1, rateLimitStatus=null, version=3.0.3}
W/System.err(24915): at twitter4j.internal.http.HttpClientImpl.request(HttpClientImpl.java:192)
W/System.err(24915): at twitter4j.internal.http.HttpClientWrapper.request(HttpClientWrapper.java:61)
W/System.err(24915): at twitter4j.internal.http.HttpClientWrapper.get(HttpClientWrapper.java:89)
W/System.err(24915): at twitter4j.TwitterBaseImpl.fillInIDAndScreenName(TwitterBaseImpl.java:126)
W/System.err(24915): at twitter4j.TwitterImpl.verifyCredentials(TwitterImpl.java:592)
W/System.err(24915): at com.regaliz.helpers.TwitterManager$2.run(TwitterManager.java:140)
W/System.err(24915): at java.lang.Thread.run(Thread.java:856)
W/System.err(24915): Caused by: java.io.IOException: Received authentication challenge is null
W/System.err(24915): at libcore.net.http.HttpURLConnectionImpl.processAuthHeader(HttpURLConnectionImpl.java:397)
W/System.err(24915): at libcore.net.http.HttpURLConnectionImpl.processResponseHeaders(HttpURLConnectionImpl.java:345)
W/System.err(24915): at libcore.net.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:276)
W/System.err(24915): at libcore.net.http.HttpURLConnectionImpl.getResponseCode(HttpURLConnectionImpl.java:479)
W/System.err(24915): at twitter4j.internal.http.HttpResponseImpl.<init>(HttpResponseImpl.java:34)
W/System.err(24915): at twitter4j.internal.http.HttpClientImpl.request(HttpClientImpl.java:156)
W/System.err(24915): ... 6 more
This is the function modified to make use of verifyCredentials:
private void init_twitter_2(final String tok, final String sec) throws TwitterException {
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setDebugEnabled(true)
.setOAuthConsumerKey(Conf.OAUTH_CONSUMER_KEY)
.setOAuthConsumerSecret(Conf.OAUTH_CONSUMER_SECRET);
// .setOAuthAccessToken(tok)
// .setOAuthAccessTokenSecret(sec);
TwitterFactory tf = new TwitterFactory(cb.build());
mTwitter=tf.getInstance();
Log.d(TAG, "init_twitter_2 "+tok+","+sec);
new Thread(new Runnable(){
#Override
public void run() {
// TODO Auto-generated method stub
User u;
try {
/** also tried setting token&secret like this, instead of in the builder-->no success */
mTwitter.setOAuthAccessToken(new AccessToken(tok,sec));
u = mTwitter.verifyCredentials();
Log.d(TAG, "User: "+u.getName());
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}}).start();
}
There are stupid people, stupid people, stupid people, and then it's me. For one week I've been struggling with the code, tracing Twitter4j, replicating oauths with curl, suspecting of garbage-collected activities, tracing DDMS, calculating hashes on tokens .... only to find I had 2 instances of Conf.OAUTH_CONSUMER_xxxxx with different values.
As the stuff came from constants, and the names were similar, I didn't realized that.
sigh -- 50 reputation points down the toilet!
I am working on an app that, among other things, authenticates with Google to grab a user's profile picture and name from their Google account. At present, I am just trying to print out the token received from Google as a debug measure, to verify that it is working that far. However, I consistently have a null token (it doesn't appear to be set at all), giving the following error:
java.lang.NullPointerException
com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
com.sp.norsesquare.froyo.NorseSquare$LoginAsyncTask.doInBackground(NS.java:465)
com.sp.norsesquare.froyo.NorseSquare$LoginAsyncTask.doInBackground(NS.java:1)
android.os.AsyncTask$2.call(AsyncTask.java:287)
java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:305)
java.util.concurrent.FutureTask.run(FutureTask.java:137)
android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1076)
java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:569)
java.lang.Thread.run(Thread.java:856)
I am using an custom AsyncTask class to get the data and return it to the main activity, this is defined as a public class within the main activity.
public class LoginAsyncTask extends AsyncTask<String, Void, String>
{
NS ns;
String email;
String mScope;
String authToken;
Context context;
Bundle bundle;
public LoginAsyncTask(String e)
{
email = e;
bundle = new Bundle();
}
protected void onPreExecute()
{
Log.i("BEGIN","Getting authtoken");
}
protected String doInBackground(String... args)
{
try
{
authToken = GoogleAuthUtil.getToken(context, email, "oauth2:"+"https://www.googleapis.com/auth/userinfo.profile", bundle);
Log.i("MESSAGEGEGEGE","YOUR TOKEN = "+authToken);
}
catch (UserRecoverableAuthException recoverableException) {
Toast.makeText(context, "UserRecoverableException Occurred", Toast.LENGTH_LONG).show();
Log.e("GOOGLEAUTH","UserRecoverableException Triggered");
Intent recoveryIntent = recoverableException.getIntent();
} catch (GoogleAuthException authEx) {
Log.e("MESSAAGEGEG", "Unrecoverable authentication exception: " + authEx.getMessage(), authEx);
} catch (IOException ioEx) {
Log.i("MESSAGEGEGE", "transient error encountered: " + ioEx.getMessage());
}
catch (Exception e) {
e.printStackTrace();
}
return authToken;
}
protected void onProgressUpdate(Integer... progress)
{
Log.i("PROGRESS","Getting somewhere");
}
protected void onPostExecute(String result)
{
Log.i("GOOGLEAUTH", "Returning Received Google Token");
googleAuthToken = result;
//This is a variable defined in the main activity
}
}
I am somewhat lost as to where this is coming from, and cannot find anything anywhere. I would deeply appreciate your help, thanks!
As suggested by Arhimed, the context you're passing to getToken() is likely null. This happened to me when I was cobbling some things together for quick and dirty testing. Passing it a valid context resolved the error.
I am working on an Android Honeycomb (v3.0) application that has a requirement of communicating with the Google Calendar API. I would like to allow my application to access a particular Google account's Calendar data in order to read and create events.
Unfortunately, I ran into a problem with authorization using OAuth2. Here's what I have so far:
1) The Google account whose calendar I would like to access is registered within the Android device I am working with.
2) I enabled the Calendar API within the Google APIs Console on the account.
3) I am able to access this account using the following code:
AccountManager accountManager = AccountManager.get(this.getBaseContext());
Account[] accounts = accountManager.getAccountsByType("com.google");
Account acc = accounts[0]; // The device only has one account on it
4) I would now like to obtain an AuthToken for use when communicating with the calendar. I followed this tutorial, but converted everything to work with Google Calendar instead of Google Tasks. I successfully retrieve an authToken from the AccountManager with the account I would like to use by using getAuthToken with AUTH_TOKEN_TYPE == "oauth2:https://www.googleapis.com/auth/calendar".
5) Here's where the problems begin. I am now at this point:
AccessProtectedResource accessProtectedResource = new GoogleAccessProtectedResource(tokens[0]); // this is the correct token
HttpTransport transport = AndroidHttp.newCompatibleTransport();
Calendar service = Calendar.builder(transport, new JacksonFactory())
.setApplicationName("My Application's Name")
.setHttpRequestInitializer(accessProtectedResource)
.build();
service.setKey("myCalendarSimpleAPIAccessKey"); // This is deprecated???
Events events = service.events().list("primary").execute(); // Causes an exception!
6) Here's the exception returned by the last line:
com.google.api.client.googleapis.json.GoogleJsonResponseException: 403 Forbidden
{
"code" : 403,
"errors" : [ {
"domain" : "usageLimits",
"message" : "Daily Limit Exceeded. Please sign up",
"reason" : "dailyLimitExceededUnreg",
"extendedHelp" : "https://code.google.com/apis/console"
} ],
"message" : "Daily Limit Exceeded. Please sign up"
}
7) According to this Google API Video (wait a minute or so to get to the applicable content), a reason for this exception may be the fact that I did not enable the API access within the Google APIs Console for the account. However, if you look at 2), you can see that I did do so.
8) To me, it seems that the problem is that I was unable to set the Simple API Access Key correctly, because the Calendar.setKey method is deprecated. Within the Google Tasks tutorial that I previously linked, the key is set using Tasks.accessKey = "key". I'm not sure how to get this working with the Calendar API, though. I have tried multiple Google accounts, which all came up with the exception from 5).
9) I would like to point out that the traditional method of using OAuth2 did work for me. Here's the code I used for that:
HttpTransport TRANSPORT = new NetHttpTransport();
JsonFactory JSON_FACTORY = new JacksonFactory();
String SCOPE = "https://www.googleapis.com/auth/calendar";
String CALLBACK_URL = "urn:ietf:wg:oauth:2.0:oob";
String CLIENT_ID = "myClientID";
String CLIENT_SECRET = "myClientSecret";
String authorizeUrl = new GoogleAuthorizationRequestUrl(CLIENT_ID, CALLBACK_URL, SCOPE).build();
String authorizationCode = "???"; // At this point, I have to manually go to the authorizeUrl and grab the authorization code from there to paste it in here while in debug mode
GoogleAuthorizationCodeGrant authRequest = new GoogleAuthorizationCodeGrant(TRANSPORT, JSON_FACTORY, CLIENT_ID, CLIENT_SECRET, authorizationCode, CALLBACK_URL);
authRequest.useBasicAuthorization = false;
AccessTokenResponse authResponse = authRequest.execute();
String accessToken = authResponse.accessToken; // gets the correct token
GoogleAccessProtectedResource access = new GoogleAccessProtectedResource(accessToken, TRANSPORT, JSON_FACTORY, CLIENT_ID, CLIENT_SECRET, authResponse.refreshToken);
HttpRequestFactory rf = TRANSPORT.createRequestFactory(access);
AccessProtectedResource accessProtectedResource = new GoogleAccessProtectedResource(accessToken);
HttpTransport transport = AndroidHttp.newCompatibleTransport();
Calendar service = Calendar.builder(transport, new JacksonFactory())
.setApplicationName("My Application's Name")
.setHttpRequestInitializer(accessProtectedResource)
.build();
Events events = service.events().list("primary").execute(); // this works!
10) Finally, my question: I would like to use the account from the AccountManager on the device itself in order to retrieve a working OAuth2 token for use with the Google Calendar API. The second method is not useful for me, because the user will have to manually go to their web browser and get the authorization code, which is not user friendly. Anyone have any ideas? Apologies for the long post, and thanks!
Try adding a JsonHttpRequestInitializer to the builder and setting your key there:
Calendar service = Calendar.builder(transport, new JacksonFactory())
.setApplicationName("My Application's Name")
.setHttpRequestInitializer(accessProtectedResource)
.setJsonHttpRequestInitializer(new JsonHttpRequestInitializer() {
public void initialize(JsonHttpRequest request) {
CalendarRequest calRequest = (CalendarRequest) request;
calRequest.setKey("myCalendarSimpleAPIAccessKey");
}
}).build();
To answer no 10 : I've basically had to do what you had to do working with the TaskSample and then use the Android GData Calendar Sample available here : http://code.google.com/p/google-api-java-client/source/browse/calendar-android-sample/src/main/java/com/google/api/client/sample/calendar/android/CalendarSample.java?repo=samples
to get the AuthToken from the AccountManager itself:
accountManager = new GoogleAccountManager(this);
settings = this.getSharedPreferences(PREF, 0);
gotAccount();
private void gotAccount() {
Account account = accountManager.getAccountByName(accountName);
if (account != null) {
if (settings.getString(PREF_AUTH_TOKEN, null) == null) {
accountManager.manager.getAuthToken(account, AUTH_TOKEN_TYPE,
true, new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle bundle = future.getResult();
if (bundle
.containsKey(AccountManager.KEY_INTENT)) {
Intent intent = bundle
.getParcelable(AccountManager.KEY_INTENT);
int flags = intent.getFlags();
flags &= ~Intent.FLAG_ACTIVITY_NEW_TASK;
intent.setFlags(flags);
startActivityForResult(intent,
REQUEST_AUTHENTICATE);
} else if (bundle
.containsKey(AccountManager.KEY_AUTHTOKEN)) {
setAuthToken(bundle
.getString(AccountManager.KEY_AUTHTOKEN));
// executeRefreshCalendars();
}
} catch (Exception e) {
handleException(e);
}
}
}, null);
} else {
// executeRefreshCalendars();
}
return;
}
chooseAccount();
}
private void chooseAccount() {
accountManager.manager.getAuthTokenByFeatures(
GoogleAccountManager.ACCOUNT_TYPE, AUTH_TOKEN_TYPE, null,
ExportClockOption.this, null, null,
new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
Bundle bundle;
try {
bundle = future.getResult();
setAccountName(bundle
.getString(AccountManager.KEY_ACCOUNT_NAME));
setAuthToken(bundle
.getString(AccountManager.KEY_AUTHTOKEN));
// executeRefreshCalendars();
} catch (OperationCanceledException e) {
// user canceled
} catch (AuthenticatorException e) {
handleException(e);
} catch (IOException e) {
handleException(e);
}
}
}, null);
}
void setAuthToken(String authToken) {
SharedPreferences.Editor editor = settings.edit();
editor.putString(PREF_AUTH_TOKEN, authToken);
editor.commit();
createCalendarService(authToken);
try {
Events events = service.events().list("primary").execute();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private void createCalendarService(String authToken) {
accessProtectedResource = new GoogleAccessProtectedResource(authToken);
Log.i(TAG, "accessProtectedResource.getAccessToken() = "
+ accessProtectedResource.getAccessToken());
JacksonFactory jsonFactory = new JacksonFactory();
service = com.google.api.services.calendar.Calendar
.builder(transport, jsonFactory)
.setApplicationName("Time Journal")
.setJsonHttpRequestInitializer(
new JsonHttpRequestInitializer() {
#Override
public void initialize(JsonHttpRequest request) {
CalendarRequest calendarRequest = (CalendarRequest) request;
calendarRequest
.setKey("<YOUR SIMPLE API KEY>");
}
}).setHttpRequestInitializer(accessProtectedResource)
.build();
}