As google tasks has no public api I want to write workaround and request data like a browser and then parse results to further display.
To access data I've implemented a OAuth authentication with google to access this url: https://mail.google.com/
For OAuth I've used sign-post library and it works well.
The problem is when I am trying to access https://mail.google.com/tasks/ig with signed request it returns me login page instead of desired list with tasks.
To be more specific here is my code:
public class GoogleOAuthActivity extends Activity {
private static final String TAG = GoogleOAuthActivity.class.getSimpleName();
private CommonsHttpOAuthProvider provider;
private CommonsHttpOAuthConsumer consumer;
@Override
@SuppressWarnings("unchecked")
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
provider = new CommonsHttpOAuthProvider(OAuthPrefs.GET_REQUEST_TOKEN_URL, OAuthPrefs.GET_ACCESS_TOKEN_URL,
OAuthPrefs.TOKEN_AUTHORIZATION_URL);
consumer = new CommonsHttpOAuthConsumer(OAuthPrefs.CONSUMER_KEY, OAuthPrefs.CONSUMER_SECRET);
consumer.setMessageSigner(new HmacSha1MessageSigner());
Log.v(TAG, "Starting google authentication activity");
new RequestGoogleOAuth(this, provider, consumer).execute();
}
@Override
@开发者_JS百科SuppressWarnings("unchecked")
public void onNewIntent(Intent intent) {
super.onNewIntent(intent);
final Uri uri = intent.getData();
if (uri != null && uri.getScheme().equals(OAuthPrefs.CALLBACK_SCHEME)) {
Log.v("OAUTH MAIN", "STARTING STAGE TWO");
new ConfirmGoogleOAuthTask(this, provider, consumer).execute(uri);
finish();
}
}
}
The first OAuth stage
public class RequestGoogleOAuth extends OAuthGoogleTask {
public static final String TAG = RequestGoogleOAuth.class.getSimpleName();
public RequestGoogleOAuth(Context context, CommonsHttpOAuthProvider provider, CommonsHttpOAuthConsumer consumer) {
super(context, provider, consumer);
}
protected Object doInBackground(Object... params) {
final String TAG = getClass().getName();
try {
final String url = provider.retrieveRequestToken(consumer, OAuthPrefs.CALLBACK_URL);
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)).setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP
& Intent.FLAG_ACTIVITY_NO_HISTORY & Intent.FLAG_FROM_BACKGROUND);
context.startActivity(intent);
Log.v(TAG, "Request google authentication");
} catch (Exception e) {
Log.e(TAG, "ERROR during google authentication request", e);
}
return null;
}
}
The second OAuth stage and attempt to access google tasks
public class ConfirmGoogleOAuthTask extends OAuthGoogleTask {
public ConfirmGoogleOAuthTask(Context context, CommonsHttpOAuthProvider provider, CommonsHttpOAuthConsumer consumer) {
super(context, provider, consumer);
}
@Override
public Object doInBackground(Object... params) {
final Uri uri = (Uri) params[0];
final String TAG = getClass().getName();
final SharedPreferences prefs = context.getSharedPreferences(OAuthPrefs.PREF_NAME, Context.MODE_PRIVATE);
final String oauthVerifier = uri.getQueryParameter(OAuth.OAUTH_VERIFIER);
try {
provider.retrieveAccessToken(consumer, oauthVerifier);
final Editor edit = prefs.edit();
edit.putString(OAuth.OAUTH_TOKEN, consumer.getToken());
edit.putString(OAuth.OAUTH_TOKEN_SECRET, consumer.getTokenSecret());
edit.commit();
CommonsHttpOAuthConsumer consumer = new CommonsHttpOAuthConsumer(OAuthPrefs.CONSUMER_KEY, OAuthPrefs.CONSUMER_SECRET);
consumer.setMessageSigner(new HmacSha1MessageSigner());
consumer.setTokenWithSecret(consumer.getToken(), consumer.getTokenSecret());
HttpClient httpClient = HttpUtils.createHttpClient();
HttpGet httpGet = new HttpGet(consumer.sign("https://mail.google.com/tasks/ig"));
HttpResponse response = httpClient.execute(httpGet);
int statusCode = response.getStatusLine().getStatusCode();
Log.d(TAG, "Status code = " + statusCode);
if (statusCode == HttpStatus.SC_OK) {
String xml = ConvertUtils.convertStreamToString(response.getEntity().getContent(), true);
Log.d(TAG, "XML = " + xml);
}
Log.v(TAG, "Successfully receive access token");
} catch (Exception e) {
Log.e(TAG, "ERROR during request for access token", e);
}
return null;
}
}
At this line:
String xml = ConvertUtils.convertStreamToString(response.getEntity().getContent(), true);
Log.d(TAG, "XML = " + xml);
I can see that I receive "Login page"
I think the reason is that google doesn't provide access to this service and even I've already authenticated with OAuth it restricts my access to this resource (even I've defined scope as https://mail.google.com/). I am not sure how to implement it right now but it looks like I need to simulate browser exactly how it interacts with google (retrieve and send appropriate coockies). But I am asking, because I am not sure how to be int this situation, because as I've mentioned google tasks API has no public API (soap, rest or any other) so it's not obvious for me how to implement client for this feature...
If somebody has examples where app access google resources without public API I will be very happy to see that.
Thanks, hoply somebody knows the answer!
[updated on 5/11 to recommend the API, 4/6 to use normal ClientLogin instead of cookies]
there's now a Google Tasks API! use that instead, it's a lot easier and your code will be more maintainable. consider the rest of this answer for posterity only.
unfortunately, you're on the right track. there's no API for google tasks at all yet, OAuth or otherwise, so you'll have to do a bit of HTML scraping. :/ afaik, that's what all other third party tasks clients do right now. ClientLogin does work for auth, though, so that's something at least.
here's some shell script code that does this: http://privacylog.blogspot.com/2010/09/updated-google-tasks-api.html . details below.
first, POST
to www.google.com/accounts/ClientLogin
as described in http://code.google.com/apis/accounts/docs/AuthForInstalledApps.html#Request to get an auth token. use goanna_mobile
as the service name. (this is important!)
then, GET https://mail.google.com/tasks/m
and pass the Authorization:GoogleLogin
header with the auth token you received above. you'll get HTML back. extract the ids of the individual task lists. they're of the form 04291568652951293844:0:0.
you'll then send POSTs with JSON-encoded body to https://mail.google.com/tasks/r/d
to get the contents of each list. here's an example body:
r={action_list:
[{action_type: get_all,
action_id: 5,
list_id: 0429158965...:0:0,
get_deleted: false,
date_start: 1969-12-31,
get_archived: true
}],
client_version: 1256686
}
important notes:
- the latest_sync_point: 0 to get all tasks
- the = in r= is not url encoded to r%3D
- include the header 'AT: 1'. without it tasks returns 401 Unauthorized.
the output is more JSON, e.g.:
{latest_sync_point: 1263000002293000, response_time:1263077227, results:[], tasks:
[{id: 04291589652955....:0:38,
last_modified: 1263000002281,
name: foo bar
notes: ,
type: TASK,
deleted: false,
list_id: [0429158965...:0:0],
completed: false
},
{id: 0429158965295...:0:37,
last_modified: 1262929947949,
name: baz
notes: ,
type: TASK,
deleted:false,
list_id: [0429158965295...:0:0],
completed: false
},
...
They do have an API since a few weeks now Check here
精彩评论