First of all, there is as of now no stable Google Docs API for Android. The recently released google-api-java-client is an early alpha both in usability as well as stability terms. The currently stable gdata-java-client library does not work on Android. So what are the options?
Well, since the Google Documents List API is quite well documented, if you need to interact with the service there is nothing stopping you from wrapping the functionality in your own set of classes. However, there is one potential show-stopper: authentication.
Historically Google Apps (by which I mean Docs, Calendar etc.) have used proprietary authentication schemes using what was called AuthSub and ClientLogin. As far as I can tell from the documentation, these are now being replaced with the open standard OAuth, or rather Google is promoting the use of OAuth over the other schemes.
In the context of Android, OAuth is slightly problematic as the platform does not ship with any OAuth related APIs and the choice of third-party libraries is rather scarce. Apart from the working, yet unstable Google library, the only viable alternative I have found is oauth-signpost. The library is quite light-weight and very easy to use, and an Apache 2.0 license just adds a cherry on top of the cake.
Below I will describe how to drive the entire authentication and authorization process from an Android application using oauth-signpost, and then how to retrieve a list of spreadsheets from the users account.
For starters, let's have a look at the entire process of authentication/authorization using OAuth and let's do that in the context of the Android activity life-cycle and Google Docs. I won't be getting into details about OAuth, as you can read that elsewhere. I'm only looking into 3-legged authentication.
This is, in short, how the process looks like with OAuth and Google Docs:
- You register your app with Google to obtain a consumer key and secret,
- Your application creates an authorization request URL,
- You application opens a web browser for the user to authenticate to Google,
- The user is asked if they want to authorize your application to access their account,
- The user grants access to your application,
- Google redirects the browser to a callback URL you have provided,
- Your application retrieves the relevant credentials,
- Your application requests a long-term access token to be used in subsequent requests.
While it seems straightforward there is very little concise documentation on how to run the whole thing through on Android. Hence this "tutorial in two parts".
The most important design problem this scenario is facing is that the authentication/authorization process requires user interaction and relies on Internet access being available at the time. Our application needs to be able to handle this out-of-band, otherwise it is most likely going to exceed the time allocation threshold and will be force-closed by Android.
We will split the process into two stages:
- Build the authorization request and start a browser for the user to authenticate with Google,
- Request a long-term access token;
We also need to handle the callback from stage one and store the initial token and token secret for stage two to use. I have seen this done in a fair few ways, but the most sensible and easy to implement one is by using SharedPreferences and the very convenient OnSharedPreferencesChangedListener method.
My solution is based on a main, controller if you like, activity which implements the OnSharedPreferencesChangedListener method and runs the two stages in sequence if required. The activity is configured as "singleTask" in AndroidManifest.xml so that only a single instance of it is running at any given time. The activity also registers a custom URI scheme that we will use in our OAuth callback.
When the controller is called for the first time, its onCreate() method fires and application preferences are checked for an existing access token. If no token exists, the process of obtaining one is started, otherwise the controller exits as it has nothing to do.
If a token does not exist stage one is started as an AsyncTask. This creates, out-of-band, the authorization request and opens the browser allowing the user to authenticate, and authorize our application. The browser the redirects back to our controller activity. At this stage the onNewIntent() method will be called, where we check the intent' URI. If it matches the registered callback scheme, we launch stage two - again as an AsyncTask. This retrieves the long-term access token and stores it in application preferences. Once the commit() method is called on the relevant preference, our listener comes into action where we can (relatively safely) assume that both of the two stages have finished OK and we now have all required credentials in place.
With the credentials in place we can start querying Google services our application was authorized for.
In part two I will show you how to actually implement this process. Alternatively, you can download the entire sample project from Google.
9 comments:
Hi, I tried to get your example working, but I get:
oauth.signpost.exception.OAuthCommunicationException: Service provider responded in error: 400 (Bad Request)
Do you have any ideas why this might be occurring?
Check the response content. It usually contains good explanation of what the actual error is. I guess it's complaining about a required request parameter missing. I have switched to Google libraries after the last alpha release (1.1.0).
I am having the same problem as Greg. I am trying to run your code as is - are there changes which have to be made to get it to work (such as CONSUMER_KEY, SECRET,...)?
And if you are now using google-api-java-client, would it be possible for you to post the example using google's lib?
Thanks so much for this!
David.
David, you need to register your domain with Google and that'll give you a consumer key and secret. Use those in the sample code.
I will soon (next few weeks, time permitting) post a quite advanced sample on how to interact with Docs using Google's own libraries.
Thanks for this great example.
Would be great if you can blog about doing a post operation such as creating or copying a document.
I actually have the code (both OAuth and interaction with Google Docs) done and in active service in my app. Just need to find the time to finish the draft post :)
That would be very helpful. I am able to authenticate using your example, but am having trouble constructing a correct Post object. I think it's the "Authorization" header since I'm getting a 401.
Hi,
I tried to use your code, without any changes but it breaks up when I do the OAuth process(press the button on the Activity). Have I got to register my phone activity on google to get the CONSUMER_KEY and CONSUMER_SECRET before I run this app?
@SanaUllah: Of course.
Post a Comment