Thursday, January 24, 2013

Integrating Picasa into Android Apps

In our most recent update of the Gallery Live Wallpaper, we added Picasa support.  We were a little more ambitious this time, so we decided to experiment with letting users access their personal Picasa accounts, which are tied to their Google accounts.  What followed was an adventure through older, soon to be deprecated Google APIs, and upcoming, not quite out of beta APIs.

Google has some pretty good documentation on the Picasa web interface:

https://developers.google.com/picasa-web/docs/2.0/reference

We were actually able to use this pretty easily to run searches of publicly available Picasa photos that we could then download for use in the Gallery Live Wallpaper.

In order to use users' private accounts, though, we need to authenticate them with Google using OAuth2.

Google makes it a point through all of this to encourage people to use their client APIs rather than hand coding all of the access to various web connections.  In looking up these libraries, though, there seem to be two editions:

Google Data Java Client Library

Google APIs Client Library for Java

Primary Picasa support is in the GData library, but this library is specifically noted to not support Android.  The new Google APIs has references to it, but it's mostly in a generalized area, Google Data APIs.

It's slightly concerning that the new Google APIs libraries are still in beta, but they largely seem to work.

There are a couple incomplete examples of Picasa support immediately available from the Google Data APIs links, but we wanted to see if we could find something more substantial, especially something that included logging in with OAuth2.

Within the Google Data APIs section, there's a sample command line Picasa implementation that makes reference to a namespace, com.google.api.services.picasa.  Some Google searching revealed this source project:

https://code.google.com/p/google-api-java-client/source/browse/shared/gdata/shared-sample-picasa/src/main/java/com/google/api/services/picasa/?repo=samples&r=97e6060623a400769dfaa72dbe20e66e00a81620

This provided a good base for creating our authenticated Picasa interface, along with the command line sample.

Library Dependencies

There are two main sets of libraries we need for Picasa and authentication: the Google APIs Client Library for Java and the Google Play Services library, obtained from the Android SDK manager in the Extras section.  The latter is not very clearly spelled out.  We didn't find it for a while, and encountered some mysterious java.lang.VerifyError exceptions.  It's referenced in the Android Task-sample project setup instructions.

As a quick note, the Google documentation talks a lot about getting client IDs and secrets to register apps for proper access to the various Google APIs.  This blog post notes that for Android, you can bypass the entire system by listing your app's package name and SHA1 signing key, which the Google client libraries will pick up and use to perform the same function automatically.  You can do this at the Google APIs Console, which is the same place you set up your client ID.

Authentication Process Overview

In order to authenticate properly, the process needs to launch Google Play to let the user allow the application to access their Picasa account.  This is done through Activity.startActivityForResult(), which means that the actual initial authorization has to run through our wallpaper settings activity, rather than the backend service we use to help downloading images asynchronously.  So we launch the authentication process when the user chooses the setting to use their personal Picasa account, and we don't allow that setting to actually be set until the full authentication process completes successfully.

In addition to needing to wait for the external Google Play activity to return, we also need to account for the authentication token expiring or being invalidated.  In that case, we need to forcibly invalidate the token we're using locally, so that the next time we make an authorization request, OAuth2 correctly requests a new token.

Most of this should be invisible to the user, but because the requests and retries take a while, our "authenticating Picasa account" dialog can stay up for 30 seconds or more.  The user should just be presented with the authorization activity from Google Play the first time, and then authentication should proceed more quickly afterwards.

Also, useful for both users and developers, we can revoke access through Google at any time.  Log in to your Google account, go to your account settings, select Security, and go to Connected applications and sites.  Clicking Manage Access here will bring up a page with all your authorized apps, where you can manually revoke any authorization you choose.

Authentication Code Sample

 Using the Android Tasks sample as a base, we can use Google's classes to do the vast majority of the authentication work.

import android.accounts.Account;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;

import com.google.api.client.http.HttpTransport;
import com.google.api.services.picasa.PicasaClient;
import com.google.api.services.picasa.PicasaUrl;
import com.google.api.services.picasa.model.UserFeed;


// ......

final HttpTransport transport =
     AndroidHttp.newCompatibleTransport();

final GoogleAccountCredential credential =
     GoogleAccountCredential.usingOAuth2(
          getContext(),
          "https://picasaweb.google.com/data/" );

final Account[] accounts = credential.getAllAccounts();

credential.setSelectedAccountName( accounts[0].name );

PicasaClient client = new PicasaClient(
     transport.createRequestFactory( credential ) );

client.setApplicationName( "KF-GalleryLiveWallpaper" );

PicasaUrl url = PicasaUrl.relativeToRoot(
     "feed/api/user/default" );

try
{
     UserFeed feed = client.executeGetUserFeed( url );
}
catch ( Exception e )
{
     /* Error handling for launching Google Play authentication, old token invalidation, etc. */

}

The scope required for GoogleAccountCredential.usingOAuth2() is noted in the Picasa Web API protocol documentation.

We also found that none of the actual requests out to Google services happen until we actually execute the Picasa request.  This is why the executeGetUserFeed() line is caught, but none of the preceding setup is.

The other big thing is that we cannot run any of this on the main activity thread or UI thread.  The system specifically throws a runtime exception about this, because the OAuth2 process can block while waiting on network responses, potentially creating a deadlock.

Final Notes

There are a lot of topics only touched on here, including more detail about handling errors in the authentication process.  There's also more study to be done on the Google Atom XML parser used for the Picasa data model classes.  And while our purpose for the Gallery Live Wallpaper is simply to search and display photos, the Picasa Web API also allows uploading and editing photos.

Picasa is quite accessible within an Android application.  The trick has been to pull together all the documentation necessary to do so.

No comments:

Post a Comment