Android Coding Example: Authenticating with ClientLogin and using the Google Reader API

GOOGLE READER…. R.I.P

Feel free to carry on reading how something used to work!

Introduction

Let me start by saying that I would of killed for an article on this a few months ago when I started writing Subscriber.
The Google Reader API is well documented out there on the Internet, but getting access to actual Java code examples is something else.
I spent many hours with Chrome, Curl, Java and Wireshark working out how to converse with Google Reader and eventually cracked it.
Anyway, moving swiftly on.. In this article I will demonstrate (with Java code examples) how to authenticate with Google
Reader and how to use certain parts of its API.

Google Reader Authentication

To use Google Reader’s API you need to authenticate using “ClientLogin”.
ClientLogin is intended for installed (desktop/mobile) applications. With this method of authentication, the application using Google Data APIs directly handles the username and password of the user.
An authentication request for ClientLogin takes a username, password, and service name as form post variables and yields a response with several tokens – one of which can be used to make requests to the Google Data service.
The first part of the process is a POST (with some data) to https://www.google.com/accounts/ClientLogin . The response of which is;
SID=DQAAAHYBADCv2pSv7nflacDNwz3zEDUGtrSvNVDcpkSfddi77b3U5sEaHmP8YLWhmA36F9rk85mL8J5dqo4apn0T1vKz0fPGI9Xtnuet6cuE2ZzYvrNIwbSC_HjTqF4zudNQnnlDuD2wqZT-g1qXI8KhGAQZV4NexHZoQPlabTsGuRZeIBxj1A
LSID=EUBBBIaBADCl-kNxvRVmcQghpt3cqSMfEooKR9flLOUZqwgP9OrZS83gse-KSdTNeXhxsET7FYenDhceP9lIPOmesH-t9qh-AWUHjjMdZEbUNeF9mWyzln6Z-FajaiG-cVFkqW0ZJ8ZbnCP30xXj6xFK6QxaAcqy_9Pej8jhEnxS9E61ftQGPg
Auth=EUBBIacAAADK-kNxvRVmcQghpt3cqSMfEooLNMflLNIQqwgP9OrZS83gs-KSdTNeXhxsET7FYePWmaD8Vsy1V4LSUGMUP48Je2TO8OcjBj6HgAtPhiZeX-gKDfagZDK44j4n-Tkb44nhOnp2_QPSnBj3Z2vYwOEDjjG3Q53aQVC2132JKOuGh
We next need to get a Token. In order to get a “Token” we need to use the Auth key. The big long string after the “Auth=”. This requires a GET (with data) to http://www.google.com/reader/api/0/token. This returns a Token similar to “57LiuCxazGzwl6cVPAGnMg“.
Once you have this Token you are able to make API calls to Google Reader. I suggest looking at the further reading for more information on the Google Reader API.

How to connect to Google Reader and parse its results

When I first started coding Subscriber, I was using the inbuilt Java net libraries to converse with http. Once I started
having to work out how to parse html responses I found a fantastic class called JSOUP written by
With this class you are able to do GETS and POSTS with ease and parse HTML with very little work. I urge you to check out
the JSOUP site and get stuck in.
Onto the code!

Pre-requisites:

Eclipse IDE
Latest JSOUP Jar file added to your project build path
My Func_String.jar file added to your project build path (available here)

Pre-requisite steps:

  1. Download the latest jsoup jar file
  2. Open Eclipse and right-click your project and select Properties
  3. Select Java Build Path
  4. Select Libraries
  5. Select Add Jars
  6. Find the download JSOUP Jar and select OK
  7. Select OK

The JSOUP class is now available to import into your project.

Code examples:

Variables

private static final String _AUTHPARAMS = "GoogleLogin auth=";
private static final String _GOOGLE_LOGIN_URL = "https://www.google.com/accounts/ClientLogin";
private static final String _READER_BASE_URL  = "http://www.google.com/reader/";
private static final String _API_URL = _READER_BASE_URL + "api/0/";
private static final String _TOKEN_URL = _API_URL + "token";
private static final String _USER_INFO_URL = _API_URL + "user-info";
private static final String _USER_LABEL = "user/-/label/";
private static final String _TAG_LIST_URL = _API_URL + "tag/list";
private static final String _EDIT_TAG_URL = _API_URL + "tag/edit";
private static final String _RENAME_TAG_URL = _API_URL + "rename-tag";
private static final String _DISABLE_TAG_URL = _API_URL + "disable-tag";
private static final String _SUBSCRIPTION_URL = _API_URL + "subscription/edit";
private static final String _SUBSCRIPTION_LIST_URL = _API_URL + "subscription/list";

Imports

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

Getting a Google Authentication Key – Returns an authentication key as a string

  /**
   * Returns a Google Authentication Key
   * Requires a Google Username and Password to be sent in the POST headers
   * to http://www.google.com/accounts/ClientLogin
   *
   * @param  GoogleGoogle_Username Google Username
   * @param  Google_Password Google Password
   * @return      Google authorisation token
   * @see         getGoogleToken
   */
  public static String getGoogleAuthKey(String _USERNAME, String _PASSWORD) throws UnsupportedEncodingException, IOException {
     Document doc = Jsoup.connect(_GOOGLE_LOGIN_URL)
    .data("accountType", "GOOGLE",
        "Email", _USERNAME,
        "Passwd", _PASSWORD,
        "service", "reader",
        "source", "<your app name>")
    .userAgent("<your app name>")
    .timeout(4000)
    .post();
 
    // RETRIEVES THE RESPONSE TEXT inc SID and AUTH. We only want the AUTH key.
    String _AUTHKEY = doc.body().text().substring(doc.body().text().indexOf("Auth="), doc.body().text().length());
    _AUTHKEY = _AUTHKEY.replace( "Auth=","" );
    return _AUTHKEY;
    }

Getting a Google Reader Token – Returns a Google token as a string

/**
   * Returns a Google Token
   * Requires a Google Username, Password and Auth key to be sent in the GET
   * to http://www.google.com/reader/api/0/token
   *
   * @param  Google_Username Google Username
   * @param  Google_Password Google Password
   * @return      Google authorisation token
   * @see         getGoogleAuthKey
   */
public static String getGoogleToken(String _USERNAME, String _PASSWORD) throws UnsupportedEncodingException, IOException {
    Document doc = Jsoup.connect(_TOKEN_URL)
    .header("Authorization", _AUTHPARAMS + getGoogleAuthKey(_USERNAME,_PASSWORD))
    .userAgent("<your app name")
    .timeout(4000)
    .get();
 
    // RETRIEVES THE RESPONSE TOKEN
    String _TOKEN = doc.body().text();
    return _TOKEN;
  }

Retrieving Google Reader User Info

/**
   * Returns Google Reader User Info
   * Requires a Google Username, Password and AUTH key to be sent in the POST
   * to http://www.google.com/reader/api/0/user-info
   *
   * @param  GoogleGoogle_Username Google Username
   * @param  Google_Password Google Password
   * @return      Google Reader User Info
   * @see         getGoogleToken
   */
public static String getUserInfo(String _USERNAME, String _PASSWORD) throws UnsupportedEncodingException, IOException {
      Document doc = Jsoup.connect(_USER_INFO_URL)
      .header("Authorization", _AUTHPARAMS + getGoogleAuthKey(_USERNAME,_PASSWORD))
    .userAgent("<your app name>")
    .timeout(4000)
    .get();
 
      // RETRIEVES THE RESPONSE USERINFO
      String _USERINFO = doc.body().text();
      return _USERINFO;
  }

Retrieving Google Reader User ID

/**
   * Returns Google User ID
   * Requires a Google Username and Password to be sent in the POST headers
   * to http://www.google.com/accounts/ClientLogin
   *
   * @return      Google User ID
   * @see         getGoogleToken, getGoogleAuthKey
   */
public static String getGoogleUserID(String _USERNAME, String _PASSWORD) throws UnsupportedEncodingException, IOException {
      /* USERINFO RETURNED LOOKS LIKE
       * {"userId":"14577161871823252783",
       * "userName":"<username>","userProfileId":"<21 numeric numbers",
       * "userEmail":"<username>@gmail.com",
       * "isBloggerUser":true,
       * "signupTimeSec":1159535065}
       */
      String _USERINFO = getUserInfo(_USERNAME, _PASSWORD);
      String _USERID = (String) _USERINFO.subSequence(11, 31);
      return _USERID;
  }

Retrieving Google Reader Tags – Returns an array of Tags (not starred or shared)

public static String[] getTagList(String _USERNAME, String _PASSWORD) {
    Log.d(_APP_TAG, "METHOD: getTagList()");
    ArrayList<String> _TAGTITLE_ARRAYLIST = new ArrayList<String>();
    String _TAG_LABEL = null;
    try {
      _TAG_LABEL = "user/" + getGoogleUserID(_USERNAME,_PASSWORD) + "/label/";
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
 
    Document doc = null;
    try {
      doc = Jsoup.connect(_TAG_LIST_URL)
      .header("Authorization", _AUTHPARAMS + getGoogleAuthKey(_USERNAME,_PASSWORD))
      .userAgent("<your app name>")
      .timeout(6000)
      .get();
    } catch (IOException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
 
      Elements links = doc.select("string");
      for (Element link : links) {
        String tagAttrib = link.attr("name");
        String tagText = link.text();
        if(Func_Strings.FindWordInString(tagText, _TAG_LABEL)) {
          _TAGTITLE_ARRAYLIST.add(tagText.substring(32));
        }
      }
 
    String[] _TAGTITLE_ARRAY = new String[_TAGTITLE_ARRAYLIST.size()];
    _TAGTITLE_ARRAYLIST.toArray(_TAGTITLE_ARRAY);
    return _TAGTITLE_ARRAY;
  }

Retrieving Google Reader Subscriptions – Returns and array of Subscriptions

public static String[] getSubList(String _USERNAME, String _PASSWORD) throws UnsupportedEncodingException, IOException {
    ArrayList<String> _SUBTITLE_ARRAYLIST = new ArrayList<String>();
 
    Document doc = Jsoup.connect(_SUBSCRIPTION_LIST_URL)
      .header("Authorization", _AUTHPARAMS + getGoogleAuthKey(_USERNAME,_PASSWORD))
    .userAgent("<your app name>")
    .timeout(5000)
    .get();
 
      Elements links = doc.select("string");
      for (Element link : links) {
        String tagAttrib = link.attr("name");
        String tagText = link.text();
          _SUBTITLE_ARRAYLIST.add(tagText);
      }
 
    String[] _SUBTITLE_ARRAY = new String[_SUBTITLE_ARRAYLIST.size()];
    _SUBTITLE_ARRAYLIST.toArray(_SUBTITLE_ARRAY);
    return _SUBTITLE_ARRAY;
  }
These methods can all be bundled into a class and a test class written to connect to Google Reader.
Example test class;
public static void main(String[] args) throws IOException {
// Get Auth Key
System.out.println(Func_JSOUP.getGoogleAuthKey(_USERNAME, _PASSWORD));
 
// Get Token
System.out.println(Func_JSOUP.getGoogleToken(_USERNAME, _PASSWORD));
 
// Get UserID
System.out.println(Func_JSOUP.getGoogleUserID(_USERNAME, _PASSWORD));
 
// Get Reader TAG TITLES
String[] _TAG_TITLES = Func_JSOUP.getTagList(_USERNAME, _PASSWORD);
for(int i = 0; i < _TAG_TITLES.length; i++){
System.out.println(_TAG_TITLES[i]);
}
 
// Get Reader SUBSCRIPTION TITLES
String[] _SUB_TITLES = Func_JSOUP.getSubList(_USERNAME, _PASSWORD);
for(int i = 0; i < _SUB_TITLES.length; i++){
System.out.println(_SUB_TITLES[i]);
}
}
I hope you have found this article useful for your project.
If you have anything further to share or wish to comment feel free to do so.

Further reading:

  1. #1 by Ondra on October 15, 2010 - 1:26 am

    Hello, thanks for nice Documentation. Google should hire you. Could you describe method, how to track another functions of Google Reader?

  2. #2 by Carroll B. Merriman on November 23, 2010 - 6:22 pm

    This is great stuff, thanks!

  3. #3 by Rajat Gupta on September 9, 2011 - 11:35 am

    very helpful
    thanks alot who share this

  4. #4 by Alex on October 23, 2011 - 5:16 am

    Thanks for the code snippets. They helped a lot.

    You might want to mention that you also need the Func_strings class, which can be downloaded from here: http://code.google.com/p/android-ced/source/browse/bbcbc/trunk/src/uk/co/chrisdadswell/bbcbc/Func_Strings.java

    Alex

  5. #5 by Kimi on December 28, 2011 - 10:09 pm

    Hi,

    can you please tell me how to retrieve a list of starred feeds?

    Thanks a lot.

  6. #6 by Vishal Kotcherlakota on January 29, 2012 - 4:14 am

    This is a fantastic piece of information! I’m a little concerned about the password, though. Does the password itself get transmitted over HTTP in cleartext? :scared:

  7. #7 by Balkrishna Rawool on March 30, 2012 - 11:15 pm

    Great work.. Simplest of the Google Reader Java APIs I have seen.
    One question though. I tried implementing a method getFeeds() to get feeds for a particular subscription using same mechanism as depicted here. But it failed everytime as the returned html contained: “This is taking longer than usual. Try reloading the page.”
    Any help is most welcome.

    Thanks in advance.

    • #8 by ChrisD on April 2, 2012 - 8:48 am

      Hi Balkrishna,

      Thanks for leaving such a kind comment.

      Unfortunately I don’t really use or maintain this code any more as I have other commitments. The only thing I would say is that maybe you need a fresher AUTH token prior to getting the feeds?

      I hope your coding goes well.

      Thanks,
      Christian.

  8. #9 by MUKUL SINHA on May 3, 2012 - 10:28 am

    Hi Viewers/Programmers
    Kindly suggest how I learn more Android Programming as I have knowledge of java/Ad-java

    Thanks
    Mukul Sinha

    • #10 by ChrisD on May 3, 2012 - 1:30 pm

      Hi Mukul,

      Thanks for leaving a comment on the site.

      I suggest you purchase some books and read/do the samples within them. Then try and think of a basic app you want to develop and go from there.

      I read the books on this site – http://commonsware.com/Android/ And they were very helpful.

      Cheers,
      Chris.

  9. #11 by sree on July 2, 2013 - 8:02 am

    Hi i want to integrate it with android .. please help me to work with android example. i am new to application development… thank you for your support..

  10. #12 by Om on December 13, 2014 - 6:14 am

    I get 403 error in getApikey fi=unction can u help me

  11. #13 by Om on December 13, 2014 - 6:15 am

    I got 403 error in getGoogleAuthKey() this function can u help me.

(will not be published)


Switch to our mobile site