If you plan to connect to the Picasa albums from your Android application, you probably know, that there is a relatively big number of different "samples" and "guides" which encourages you to use "this protocol" and "that library" saying in turn that this and that is already deprecated and should not be used anymore. Moreover majority of those samples are about different (tasks) services and therefore can not work with Picasa service without adjustments, which are not clear. I have been googling around for about four days as I tried to figure out, what is the proper way of working with Picasa albums, downloading tons of sources, including bags of jars to my "simplesample" project.
Finally I have figured out, that working with Picasa is very easy from Android if you know how to do It.
All we have to do is :
- Register our application on the Api console https://code.google.com/apis/console/ to get access to the service. Note, that Picasa service is not listed in possible services. It does not matter as you can have 0 active service to work with picasa.
- Take an OAuth2 authToken,from AccountManager
- Sign your HttpURLConnection with the authToken
Data that you obtain from Picasa service are in the Atom XML format and therefore there are very simple to parse. For more detailed reference about how to query Picasa see https://developers.google.com/picasa-web/docs/2.0/developers_guide_protocol .
Here goes an activity example. The activity will list albums titles to the LogCat window in Eclipse:
//FIXME This class does not deal with situation when there is not any user account in the device public class ActivityGallery extends Activity implements AccountManagerCallback<Bundle> { private final String LOGTAG = getClass().getName(); /**This is what you have got from the api console https://code.google.com/apis/console/**/ private final String CLIENT_ID = "123blahblahblah456.com"; /** * Service identification. Note the prefix oauth2 - it is necessary * See https://developers.google.com/gdata/faq?hl=cs#AuthScopes **/ private final String AUTH_TOKEN_TYPE = "oauth2:http://picasaweb.google.com/data/"; /** Type of accounts that we are interested in **/ private final String ACCOUNT_TYPE = "com.google"; /** Keys for a settings items - not auth-related stuff ;-) **/ static final String PREFKEY_ACCOUNT_NAME = "pref1_name"; static final String PREFKEY_AUTH_TOKEN = "pref2_token"; private SharedPreferences settings; private AccountManager aManager; private String accountName; private String authToken; private HttpURLConnection connection; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Inspect preferences for saved values and get AccountManager settings = getPreferences(Activity.MODE_PRIVATE); // accountName = settings.getString(PREFKEY_ACCOUNT_NAME, null); // authToken = settings.getString(PREFKEY_AUTH_TOKEN, null); aManager = AccountManager.get(this); dance(); } /** * Do the authentication dance. * At first try to use an account name and an authToken from the * SharedPreferences * If the first fails, try to get authToken for an accountName from * SharedPreferences * If the second fails, try to choose an account and get authToken for it. */ public void dance() { Log.v(LOGTAG, "dance()"); // We have got everything if (accountName != null && authToken != null) { listAlbums(true); } else { // We have to do more to obtain an authToken Account account = null; // We have got some account name, let's verify if it is still valid if (accountName != null) { Account[] accounts = aManager.getAccountsByType(ACCOUNT_TYPE); for (Account acc : accounts) { if (acc.name.equals(accountName)) { account = acc; break; } } } Bundle options=new Bundle(); if (account != null) { // Well, we don't have an authToken, but we have a valid account aManager.getAuthToken(account, AUTH_TOKEN_TYPE, options, this, this, null); } else { // Hmmm, we have to choose an account to use Log.v(LOGTAG, "chooseAccount()"); aManager.getAuthTokenByFeatures(ACCOUNT_TYPE, AUTH_TOKEN_TYPE, null, this, options, null, this, null); } } } /** * Callback method from getAuthTokenByFeatures() and getAuthToken(). All we * have to do * is remember an authToken and an accountName and then begin to work. * * @param future */ @Override public void run(AccountManagerFuture<Bundle> future) { Log.v(LOGTAG, "AccountManagerCallback.run()"); try { Bundle bundle = future.getResult(); if (bundle.containsKey(AccountManager.KEY_AUTHTOKEN)) { SharedPreferences.Editor editor = settings.edit(); authToken = bundle.getString(AccountManager.KEY_AUTHTOKEN); Log.v(LOGTAG, "setAuthToken()" + authToken); editor.putString(PREFKEY_AUTH_TOKEN, authToken); accountName = bundle.getString(AccountManager.KEY_ACCOUNT_NAME); Log.v(LOGTAG, "setAccountName()" + accountName); editor.putString(PREFKEY_ACCOUNT_NAME, accountName); editor.commit(); listAlbums(true); } } catch (AuthenticatorException _e) { _e.printStackTrace(); // TODO if the authenticator failed to respond } catch (OperationCanceledException _e) { _e.printStackTrace(); // TODO user cancelled the operation } catch (IOException _e) { _e.printStackTrace(); // TODO if I/O problem creating a new auth token, usually because of // network trouble } } public void listAlbums(boolean _ready) { URL url = null; try { url = new URL("https://picasaweb.google.com/data/feed/api/user/default"); } catch (MalformedURLException e) { // TODO Auto-generated catch block e.printStackTrace(); } try { connection = (HttpURLConnection) url.openConnection(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } connection.addRequestProperty("X-GData-Client", CLIENT_ID);//This may be is not necessary connection.setRequestProperty("GData-Version", "2"); connection.setRequestProperty("Authorization", "OAuth " + authToken); try { Log.v(LOGTAG, "Response" + connection.getResponseCode() + " (" + connection.getResponseMessage() + ")"); if(connection.getResponseCode()==401 || connection.getResponseCode()==403){ aManager.invalidateAuthToken(ACCOUNT_TYPE, authToken); authToken=null; //Dance again to refresh the authToken //- it should be treated more carefully to prevent an eternal dance dance(); return; } //List user's albums DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); try { DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); Document doc = dBuilder.parse(connection.getInputStream()); Element root = doc.getDocumentElement(); NodeList nodes = root.getElementsByTagName("entry"); for (int i = 0; i < nodes.getLength(); i++) { Element element = (Element)nodes.item(i); Element title=(Element)element.getElementsByTagName("title").item(0); Log.v(LOGTAG, title.getChildNodes().item(0).getNodeValue()+""); } } catch (ParserConfigurationException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (SAXException e) { // TODO Auto-generated catch block e.printStackTrace(); } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
Note, that you will need following permissions declared in yout manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" > <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
Hope this will work for you.