Im trying to access youtube account with account manager, meaning i want to access youtube with a account linked to my device and with this get youtube token to access user playlist and whatever. Freedi application for android doing somthing like this.
I used this code to get token
am.getAuthToken(accounts[0], "youtube", 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);
intent.setFlags(intent.getFlags() & ~Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(intent, 0);
} else if (bundle.containsKey(AccountManager.KEY_AUTHTOKEN)) {
my_access_token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
onActivityResult(0,1,null);
}
} catch (Exception e) {
Log.e("TEST", e.getMessage(), e);
}
}
}, null);
but the var my_access_token filled with token that youtube not recognize...
how can i get youtube token from it? and how to get the user playlist?
you're in luck as I just finished cracking this problem on the app I'm developing.
AccountManager.get(getApplicationContext()).getAuthTokenByFeatures("com.google", "oauth2:https://gdata.youtube.com", null, this,
null, null, new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Bundle bundle = future.getResult();
String acc_name = bundle.getString(AccountManager.KEY_ACCOUNT_NAME);
String auth_token = bundle.getString(AccountManager.KEY_AUTHTOKEN);
Log.d(DEBUG_TAG, "name: " + acc_name + "; token: " + auth_token);
} catch (Exception e) {
Log.e(DEBUG_TAG, e.getClass().getSimpleName() + ": " + e.getMessage());
}
}
}, null);
I haven't used it myself in an app yet, and it might not be available on all Android devices, but my understanding is that the Google Play services now provides the best approach to getting OAuth 2 tokens (including those scoped to https://gdata.youtube.com). There's more info at
http://android-developers.blogspot.com/2012/09/google-play-services-and-oauth-identity.html
You could go with the AccountManager approach for wider compatibility, though.
The confusing part is the authTokenType parameter, it must be complete with the spec for OAuth2 access, e.g.
"oauth2:https://gdata.youtube.com"
or
"oauth2:https://www.googleapis.com/auth/tasks"
Related
I am building an android application where user will login using linkedin.
When user click on the button the default linkedin login page appear after entering the Email ID and password when I click accepted I an disable to get user details.
Below is my login code -
public void onClick(View v) {
if (v.getId() == R.id.btnLinkedin) {
oAuthService = LinkedInOAuthServiceFactory.getInstance()
.createLinkedInOAuthService(Constants.CONSUMER_KEY,
Constants.CONSUMER_SECRET);
System.out.println("oAuthService : " + oAuthService);
factory = LinkedInApiClientFactory.newInstance(
Constants.CONSUMER_KEY, Constants.CONSUMER_SECRET);
liToken = oAuthService
.getOAuthRequestToken(Constants.OAUTH_CALLBACK_URL);
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(liToken
.getAuthorizationUrl()));
Toast.makeText(getApplicationContext(), "3", Toast.LENGTH_LONG).show();
startActivity(i);
}
}
#Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Toast.makeText(getApplicationContext(), "", Toast.LENGTH_LONG).show();
try {
linkedInImport(intent);
} catch (NullPointerException e) {
e.printStackTrace();
}
}
private void linkedInImport(Intent intent) {
String verifier = intent.getData().getQueryParameter("oauth_verifier");
System.out.println("liToken " + liToken);
System.out.println("verifier " + verifier);
LinkedInAccessToken accessToken = oAuthService.getOAuthAccessToken(
liToken, verifier);
client = factory.createLinkedInApiClient(accessToken);
// client.postNetworkUpdate("LinkedIn Android app test");
Person profile = client.getProfileForCurrentUser(EnumSet.of(
ProfileField.ID, ProfileField.FIRST_NAME,
ProfileField.LAST_NAME, ProfileField.HEADLINE));
System.out.println("First Name :: " + profile.getFirstName());
System.out.println("Last Name :: " + profile.getLastName());
System.out.println("Head Line :: " + profile.getHeadline());
}
LinkedIn now provides an Android development SDK to handle these situations for you more easily, that you might want to look into:
https://developer.linkedin.com/docs/android-sdk
I know this is an old thread, but just in case anyone came here looking for answers...
LinkedIn has deprecated and stopped supporting their old mobile SDKs as per this link, so I've created a lightweight "unofficial" SDK for Android, you can use it from this GitHub repo.
We also use it in our production apps, so it should be working fine. :)
I have a problem with kitkat api while tringy to get access token of google account services, google music in my case. So, if user trying get token at first by using next method:
public String getAuthToken(Account account)
throws AuthenticatorException, IOException {
String s1;
if (account == null) {
Log.e("MusicAuthInfo", "Given null account to MusicAuthInfo.getAuthToken()", new Throwable());
throw new AuthenticatorException("Given null account to MusicAuthInfo.getAuthToken()");
}
String s = getAuthTokenType(mContext);
try {
s1 = AccountManager.get(mContext).blockingGetAuthToken(account, s, true);
} catch (OperationCanceledException operationcanceledexception) {
throw new AuthenticatorException(operationcanceledexception);
}
if (s1 == null) {
throw new AuthenticatorException("Received null auth token.");
}
return s1;
}
here i get s1 == null and the system push notification:
When user tap on notification, next dialog appear:
When user click "ok", all next iterations getting token get success.
Question: How to circumvent this confirmation or show just dialog, without click to notification ?
It's not a direct answer to your question, but you can use Google Play Services instead.
String token = GoogleAuthUtil.getToken(context, userEmail, "oauth2:https://mail.google.com/");
You just have to specify the oauth2 scope you need. For instance for Google+ you would need "https://www.googleapis.com/auth/plus.login" instead of what I post in the snippet for Gmail. You can also specify multiple scopes in one token request. The permission request pops up right away.
You can read all about it here: Authorizing with Google for REST APIs, Login scopes
Solved. Need use this method:
Bundle result = AccountManager.get(activity).getAuthToken(account, s, new Bundle(), activity, new AccountManagerCallback<Bundle>() {
#Override
public void run(AccountManagerFuture<Bundle> future) {
try {
Log.e("xxx", future.getResult().toString());
} catch (OperationCanceledException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (AuthenticatorException e) {
e.printStackTrace();
}
}
}, null).getResult();
I am creating an box android app that allows user to upload media files on their account.
I have set up my client id and client secret,it is authenticating my app too.
Uploading part is also done,but the problem i am facing is to save the auth data [which is obviously needed so user is not needed to login again and again]
Load, save and use of authentication data in Box Android API
the solution given above is not working [may b they have removed 'Utils.parseJSONStringIntoObject' method]
i can store the access token and refresh token but whats the point of saving when i cant use them to re authenticate a user
switch (requestCode)
{
case AUTHENTICATE_REQUEST:
if (resultCode == Activity.RESULT_CANCELED)
{
String failMessage = data.getStringExtra(OAuthActivity.ERROR_MESSAGE);
Toast.makeText(this, "Auth fail:" + failMessage, Toast.LENGTH_LONG).show();
// finish();
}
else
{
BoxAndroidOAuthData oauth = data.getParcelableExtra(OAuthActivity.BOX_CLIENT_OAUTH);
BoxAndroidClient client = new BoxAndroidClient(BoxSDKSampleApplication.CLIENT_ID, BoxSDKSampleApplication.CLIENT_SECRET, null, null);
client.authenticate(oauth);
String ACCESS_TOKEN=oauth.getAccessToken();
String REFRESH_TOKEN=oauth.getRefreshToken();
Editor editor = prefs.edit();
editor.putString("ACCESS_TOKEN", ACCESS_TOKEN);
editor.putString("REFRESH_TOKEN", REFRESH_TOKEN);
editor.commit();
BoxSDKSampleApplication app = (BoxSDKSampleApplication) getApplication();
client.addOAuthRefreshListener(new OAuthRefreshListener()
{
#Override
public void onRefresh(IAuthData newAuthData)
{
Log.d("OAuth", "oauth refreshed, new oauth access token is:" + newAuthData.getAccessToken());
//---------------------------------
BoxOAuthToken oauthObj=null;
try
{
oauthObj=getClient().getAuthData();
}
catch (AuthFatalFailureException e)
{
e.printStackTrace();
}
//saving refreshed oauth object in client
BoxAndroidOAuthData newAuthDataObj=new BoxAndroidOAuthData(oauthObj);
getClient().authenticate(newAuthDataObj);
}
});
app.setClient(client);
}
i have referred https://github.com/box/box-android-sdk-v2/tree/master/BoxSDKSample example
can any one tell me what i am doing wrong or any alternative to authenticate user using authdata,access token,refresh token?
UPDATE
refreshing token as they have said
'Our sdk auto refreshes OAuth access token when it expires. You will want to listen to the refresh events and update your stored token after refreshing.'
mClient.addOAuthRefreshListener(new OAuthRefreshListener()
{
#Override
public void onRefresh(IAuthData newAuthData)
{
Log.d("OAuth", "oauth refreshed, new oauth access token is:" + newAuthData.getAccessToken());
try
{
oauthObj=mClient.getAuthData();
mClient.authenticate(newAuthData);
String authToken=null;
//Storing oauth object in json string format
try
{
authToken = new BoxJSONParser(new AndroidBoxResourceHub()).convertBoxObjectToJSONString(newAuthData);
prefs.edit().putString("BOX_TOKEN", authToken).commit();
//saving authToken in shared Preferences
mClient.authenticate(newAuthData);
String ACCESS_TOKEN=newAuthData.getAccessToken();
String REFRESH_TOKEN=newAuthData.getRefreshToken();
Log.v("New Access token ", oauthObj.getAccessToken());
Log.v("New Refresh token ", oauthObj.getRefreshToken());
editor.putString("ACCESS_TOKEN", ACCESS_TOKEN);
editor.putString("REFRESH_TOKEN", REFRESH_TOKEN);
prefs.edit().putString("BOX_TOKEN", authToken).commit();
editor.commit();
}
catch (BoxJSONException e1)
{
e1.printStackTrace();
}
Log.v("Token Refreshed", " ");
}
catch (AuthFatalFailureException e)
{
e.printStackTrace();
}
}
});
app.setClient(mClient);
}
onClientAuthenticated();
In main activity,fetching stored token
try
{
stored_oauth_token=prefs.getString("BOX_TOKEN", null);
authData = new BoxJSONParser(new AndroidBoxResourceHub()).parseIntoBoxObject(stored_oauth_token, BoxAndroidOAuthData.class);
}
catch (BoxJSONException e)
{
e.printStackTrace();
}
mClient = new BoxAndroidClient(BoxSDKSampleApplication.CLIENT_ID, BoxSDKSampleApplication.CLIENT_SECRET, null, null);
mClient.authenticate(authData);
BoxSDKSampleApplication app = (BoxSDKSampleApplication) getApplication();
app.setClient(mClient);
i tried this app to upload a file after existing ,it did work
but after 60-70 odd minutes i couldn't upload file.
is there anything wrong in my code ?
This is how I initialize my Box client:
mClient = new BoxClient(BOX_CLIENT_ID, BOX_CLIENT_SECRET, null, null);
mClient.addOAuthRefreshListener(new OAuthRefreshListener() {
#Override
public void onRefresh(IAuthData newAuthData) {
try {
String authToken = new BoxJSONParser(new AndroidBoxResourceHub()).convertBoxObjectToJSONString(newAuthData);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
prefs.edit().putString("box_token", authToken).commit();
} catch (BoxJSONException e) { }
}
});
mAuthToken = prefs.getString("box_token", null);
if (mAuthToken != null) {
BoxAndroidOAuthData authData = new BoxJSONParser(
new AndroidBoxResourceHub()
).parseIntoBoxObject(mAuthToken, BoxAndroidOAuthData.class);
mClient.authenticate(authData);
}
if (!mClient.isAuthenticated()) {
Intent intent = OAuthActivity.createOAuthActivityIntent(context, BOX_CLIENT_ID, BOX_CLIENT_SECRET, false, "https://yoururl.com/");
((Activity) context).startActivityForResult(intent, BOX_AUTH_REQUEST_CODE);
}
So for the auth refresh there are a couple of things to be considered:
box client automatically refreshes OAuth tokens, you'll want to attach a OAuthRefreshListener to listen to the refresh, if you want to persist, persist the oauth data passed into the refresh listener. The listener only update your persisted oauth data, you don't need to re-authenticate in the refresh listener, sdk does the re-authenticate automatically.
When you first initiate box client, you need to authenticate either by persisted auth, or the OAuth UI. The logic should be:
check client.isAuthenticated();
2.1 If authenticated, do nothing.
2.2 if not authenticated, try to check whether there's persisted auth data. If so, authenticate by client.authenticate(oauthdata);
2.3 if 2.2 failed, start OAuth UI flow.
2.4 at last, in case of OAuthFatalFailureException, start OAuth UI flow.
We have contacted Google about this and we are on chat
The issue seems to be fixed for devices except Samsung phones.
I'm adding a Google+ sign in option to an app per the official instructions. Once the user has selected their account I would like my server to retrieve their Google+ profile info and update their profile on our site to match.
The first part - having the user select a Google account locally - seems to work just fine. When I try to request a token for the selected account, the Google auth dialog displays with the appropriate parameters; however, when I authorize the app using that dialog and re-request the token, GoogleAuthUtil.getToken(...) again throws a UserRecoverableAuthException (NeedPermission, not GooglePlayServicesAvailabilityException) and I get the same dialog asking me to approve!
This behavior is present on a Samsung S3 running Android 4.1.1 (with 3 Google accounts) and an Acer A100 running 4.0.3. It is NOT present on an HTC Glacier running 2.3.4. Instead, the HTC Glacier gives me a valid auth code. All devices have the latest iteration of Google Play Services installed and are using different Google+ accounts.
Anyone seen this before? Where can I start with debugging?
Here's the complete code - is anything obviously awry?
public class MyGooglePlusClient {
private static final String LOG_TAG = "GPlus";
private static final String SCOPES_LOGIN = Scopes.PLUS_LOGIN + " " + Scopes.PLUS_PROFILE;
private static final String ACTIVITIES_LOGIN = "http://schemas.google.com/AddActivity";
private static MyGooglePlusClient myGPlus = null;
private BaseActivity mRequestingActivity = null;
private String mSelectedAccount = null;
/**
* Get the GPlus singleton
* #return GPlus
*/
public synchronized static MyGooglePlusClient getInstance() {
if (myGPlus == null)
myGPlus = new MyGooglePlusClient();
return myGPlus;
}
public boolean login(BaseActivity requester) {
Log.w(LOG_TAG, "Starting login...");
if (mRequestingActivity != null) {
Log.w(LOG_TAG, "Login attempt already in progress.");
return false; // Cannot launch a new request; already in progress
}
mRequestingActivity = requester;
if (mSelectedAccount == null) {
Intent intent = AccountPicker.newChooseAccountIntent(null, null, new String[]{GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE}, false,
null, GoogleAuthUtil.GOOGLE_ACCOUNT_TYPE, null, null);
mRequestingActivity.startActivityForResult(intent, BaseActivity.REQUEST_GPLUS_SELECT);
}
return true;
}
public void loginCallback(String accountName) {
mSelectedAccount = accountName;
authorizeCallback();
}
public void logout() {
Log.w(LOG_TAG, "Logging out...");
mSelectedAccount = null;
}
public void authorizeCallback() {
Log.w(LOG_TAG, "User authorized");
AsyncTask<Void, Void, String> task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
String token = null;
try {
Bundle b = new Bundle();
b.putString(GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES, ACTIVITIES_LOGIN);
token = GoogleAuthUtil.getToken(mRequestingActivity,
mSelectedAccount,
"oauth2:server:client_id:"+Constants.GOOGLE_PLUS_SERVER_OAUTH_CLIENT
+":api_scope:" + SCOPES_LOGIN,
b);
} catch (IOException transientEx) {
// Network or server error, try later
Log.w(LOG_TAG, transientEx.toString());
onCompletedLoginAttempt(false);
} catch (GooglePlayServicesAvailabilityException e) {
Log.w(LOG_TAG, "Google Play services not available.");
Intent recover = e.getIntent();
mRequestingActivity.startActivityForResult(recover, BaseActivity.REQUEST_GPLUS_AUTHORIZE);
} catch (UserRecoverableAuthException e) {
// Recover (with e.getIntent())
Log.w(LOG_TAG, "User must approve "+e.toString());
Intent recover = e.getIntent();
mRequestingActivity.startActivityForResult(recover, BaseActivity.REQUEST_GPLUS_AUTHORIZE);
} catch (GoogleAuthException authEx) {
// The call is not ever expected to succeed
Log.w(LOG_TAG, authEx.toString());
onCompletedLoginAttempt(false);
}
Log.w(LOG_TAG, "Finished with task; token is "+token);
if (token != null) {
authorizeCallback(token);
}
return token;
}
};
task.execute();
}
public void authorizeCallback(String token) {
Log.w(LOG_TAG, "Token obtained: "+token);
// <snipped - do some more stuff involving connecting to the server and resetting the state locally>
}
public void onCompletedLoginAttempt(boolean success) {
Log.w(LOG_TAG, "Login attempt "+(success ? "succeeded" : "failed"));
mRequestingActivity.hideProgressDialog();
mRequestingActivity = null;
}
}
I've had this issue for a while and came up with a proper solution.
String token = GoogleAuthUtil.getToken(this, accountName, scopeString, appActivities);
This line will either return the one time token or will trigger the UserRecoverableAuthException.
On the Google Plus Sign In guide, it says to open the proper recovery activity.
startActivityForResult(e.getIntent(), RECOVERABLE_REQUEST_CODE);
When the activity returns with the result, it will come back with few extras in the intent and that is where the new token resides :
#Override
protected void onActivityResult(int requestCode, int responseCode, Intent intent) {
if (requestCode == RECOVERABLE_REQUEST_CODE && responseCode == RESULT_OK) {
Bundle extra = intent.getExtras();
String oneTimeToken = extra.getString("authtoken");
}
}
With the new oneTimeToken given from the extra, you can submit to the server to connect properly.
I hope this helps!
Its too late to reply but it may help to people having same concern in future.
They have mentioned in the tutorial that it will always throw UserRecoverableAuthException
when you invoke GoogleAuthUtil.getToken() for the first time. Second time it will succeed.
catch (UserRecoverableAuthException e) {
// Requesting an authorization code will always throw
// UserRecoverableAuthException on the first call to GoogleAuthUtil.getToken
// because the user must consent to offline access to their data. After
// consent is granted control is returned to your activity in onActivityResult
// and the second call to GoogleAuthUtil.getToken will succeed.
startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST_CODE);
return;
}
i used below code to get access code from google.
execute this new GetAuthTokenFromGoogle().execute(); once from public void onConnected(Bundle connectionHint) and once from protected void onActivityResult(int requestCode, int responseCode, Intent intent)
private class GetAuthTokenFromGoogle extends AsyncTask<Void, Integer, Void>{
#Override
protected void onPreExecute()
{
}
#Override
protected Void doInBackground(Void... params) {
// TODO Auto-generated method stub
try {
accessCode = GoogleAuthUtil.getToken(mContext, Plus.AccountApi.getAccountName(mGoogleApiClient), SCOPE);
new ValidateTokenWithPhoneOmega().execute();
Log.d("Token -- ", accessCode);
} catch (IOException transientEx) {
// network or server error, the call is expected to succeed if you try again later.
// Don't attempt to call again immediately - the request is likely to
// fail, you'll hit quotas or back-off.
return null;
} catch (UserRecoverableAuthException e) {
// Recover
startActivityForResult(e.getIntent(), RC_ACCESS_CODE);
e.printStackTrace();
} catch (GoogleAuthException authEx) {
// Failure. The call is not expected to ever succeed so it should not be
// retried.
authEx.printStackTrace();
return null;
} catch (Exception e) {
throw new RuntimeException(e);
}
return null;
}
#Override
protected void onPostExecute(Void result)
{
}
}
I have got around this issue by using a web based login. I open a url like this
String url = "https://accounts.google.com/o/oauth2/auth?scope=" + Scopes.PLUS_LOGIN + "&client_id=" + webLoginClientId + "&response_type=code&access_type=offline&approval_prompt=force&redirect_uri=" + redirect;
The redirect url then handles the response and returns to my app.
In terms of my findings on using the Google Play Services, I've found:
HTC One is 3.1.59 (736673-30) - not working
Galaxy Note is 3.1.59 (736673-36) - not working
Nexus S is 3.1.59 (736673-34) - works
And I'd like to be involved in the chat that is occurring, however I don't have a high enough reputation to do so.
I've experienced the same issue recently - it appears to be device-specific (I had it happen every time on one S3, but on another S3 running the same OS it didn't happen, even with the same account). My hunch is that it's a bug in a client app, either the G+ app or the Google Play Services app. I managed to solve the issue on one of my devices by factory resetting it (a Motorola Defy), then reinstalling the Google Play Services app, but that's a completely useless solution to tell to users.
Edit (6th Aug 2013): This seems to have been fixed for me without any changes to my code.
The first potential issue I can see is that you are calling GoogleAuthUtil.getToken() after you get the onConnected() callback. This is a problem because requesting an authorization code for your server using GoogleAuthUtil.getToken() will always show a consent screen to your users. So you should only get an authorization code for new users and, to avoid showing new users two consent screens, you must fetch an authorization code and exchange it on your server before resolving any connection failures from PlusClient.
Secondly, make sure you actually need both a PlusClient and an authorization code for your servers. You only need to get a PlusClient and an authorization code if you are intending to make calls to the Google APIs from both the Android client and your server. As explained in this answer.
These issues would only result in two consent dialogs being displayed (which is clearly not an endless loop) - are you seeing more than two consent dialogs?
I had a similar problem where an apparent auth loop kept creating {read: spamming} these "Signing In..." and Permission request dialogs while also giving out the discussed exception repeatedly.
The problem appears in some slightly-modified example code that I (and other like me, I suspect) "cargo-culted" from AndroidHive. The solution that worked for me was ensuring that only one background token-retrieval task runs at the background at any given time.
To make my code easier to follow, here's the auth flow in my app (that is almost identical to the example code on AndoidHive): Activity -> onConnected(...) -> getProfileInformation() -> getOneTimeToken().
Here's where getOneTimeToken() is called:
private void getProfileInformation() {
try {
if (Plus.PeopleApi.getCurrentPerson(mGoogleApiClient) != null) {
Person currentPerson = Plus.PeopleApi
.getCurrentPerson(mGoogleApiClient);
String personName = currentPerson.getDisplayName();
String personPhotoUrl = currentPerson.getImage().getUrl();
String personGooglePlusProfile = currentPerson.getUrl();
String email = Plus.AccountApi.getAccountName(mGoogleApiClient);
getOneTimeToken(); // <-------
...
Here's my getOneTimeToken():
private void getOneTimeToken(){
if (task==null){
task = new AsyncTask<Void, Void, String>() {
#Override
protected String doInBackground(Void... params) {
LogHelper.log('d',LOGTAG, "Executing background task....");
Bundle appActivities = new Bundle();
appActivities.putString(
GoogleAuthUtil.KEY_REQUEST_VISIBLE_ACTIVITIES,
ACTIVITIES_LOGIN);
String scopes = "oauth2:server" +
":client_id:" + SERVER_CLIENT_ID +
":api_scope:" + SCOPES_LOGIN;
String token = null;
try {
token = GoogleAuthUtil.getToken(
ActivityPlus.this,
Plus.AccountApi.getAccountName(mGoogleApiClient),
scopes,
appActivities
);
} catch (IOException transientEx) {
/* Original comment removed*/
LogHelper.log('e',LOGTAG, transientEx.toString());
} catch (UserRecoverableAuthException e) {
/* Original comment removed*/
LogHelper.log('e',LOGTAG, e.toString());
startActivityForResult(e.getIntent(), AUTH_CODE_REQUEST);
} catch (GoogleAuthException authEx) {
/* Original comment removed*/
LogHelper.log('e',LOGTAG, authEx.toString());
} catch (IllegalStateException stateEx){
LogHelper.log('e',LOGTAG, stateEx.toString());
}
LogHelper.log('d',LOGTAG, "Background task finishing....");
return token;
}
#Override
protected void onPostExecute(String token) {
LogHelper.log('i',LOGTAG, "Access token retrieved: " + token);
}
};
}
LogHelper.log('d',LOGTAG, "Task setup successful.");
if(task.getStatus() != AsyncTask.Status.RUNNING){
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR); //double safety!
} else
LogHelper.log('d',LOGTAG,
"Attempted to restart task while it is running!");
}
Please note that I have a {probably redundant} double-safety against the task executing multiple times:
if(task .getStatus() != AsyncTask.Status.RUNNING){...} - ensures that the task isn't running before attempting to execute it.
task.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR);- makes sure that copies of this task are "synchronized" (i.e. a queue is in place such that only one task of this type can executed at a given time).
P.S.
Minor clarification: LogHelper.log('e',...) is equivalent to Log.e(...) etc.
you should startactiviy in UI thread
try {
....
} catch (IOException transientEx) {
....
} catch (final UserRecoverableAuthException e) {
....
runOnUiThread(new Runnable() {
public void run() {
startActivityForResult(e1.getIntent(), AUTH_CODE_REQUEST);
}
});
}
Had the same bug with infinite loop of permission request. For me it was because time on my phone was shifted. When I check detect time automatically this bug disappeared. Hope this helps!
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();
}