Android – Google Contact API 3.0 Example

One of the application I am developing for Android had a functionality to import Google contacts. I faced quite a few issues to implement this and was not able to find any tutorial / example on internet providing detailed information.
Now that I’ve implemented this feature, thought of sharing my knowledge with the community with this blog post. Hope this is helpful.

Important Links –
Google Contact API Documentation –  https://developers.google.com/google-apps/contacts/
Google Developer Console – https://console.developers.google.com/
GData Java Client – https://code.google.com/p/gdata-java-client/downloads/
Guava Libraries – https://code.google.com/p/guava-libraries/
Contact API Scope – https://www.googleapis.com/auth/contacts.readonly

Google Project Setup – First of all, you will have to register your project with Google. To do so –
1) Login to Google Developer Console https://console.developers.google.com/project/ and create new Project.

Create_Project

2) Once project is created, we will have to generate identifiers for OAuth 2.0 authentication. Navigate to ‘APIS & AUTH’ > ‘Credentials’ and click on ‘Create new Client ID’.

Create_Project_ID

3) From Create Client ID Popup, select Application Type as ‘Installed Application’ and Select ‘Other’ for ‘Installed Application Type’ and click ‘Create Client ID’ button.

Create_Client_ID_Popup

4) This should generate the Client ID, Client Secret and Redirect URL for native application. These details will be required for authentication.

Create_Client_ID

5) Now it’s time to enable Contact API for this project. Navigate to ‘APIS & AUTH’ > ‘APIs’, search for ‘Contacts API’ and Click ‘OFF’ button. This should enable Contacts API.

Authorizing request using OAuth 2.0
Typical flow for OAuth 2.0 authentication is –
1) Application makes request to Google to fetch contacts (Scope)
2) Google shows OAuth Dialog to users for their approval
3) Once user provides approval, a short lived ‘Access Token’ is sent to requesting application
4) Application then makes request to get contacts using this Access Token

Now lets look at the actual code to authorize and fetch contacts details from an Android application.

We will create an Activity which will show OAuth window to users using WebView and once user permission is granted, we will make a call to fetch Google Contacts.

Lets start with Layout –
1) activity_import_gmail_contacts.xml – Create a layout which will be used by our Activity

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.tusharwadekar.android.ttmm.ImportGmailContactsActivity" >

    <Button
        android:id="@+id/importContactButton"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:background="#33B5E5"
        android:text="@string/importContactButton" />

    <ListView
        android:id="@+id/list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:paddingBottom="5dp"
        android:layout_above="@id/importContactButton" />

</RelativeLayout>

2) auth_dialog.xml – Create a layout which will be used to display Google OAuth window

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >
      <WebView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/webv"/>
</LinearLayout>

Create a class GetAccessToken. This class will be used to parse Access Token received from Google.

package com.tusharwadekar.android.ttmm.oauth;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.json.JSONException;
import org.json.JSONObject;
import android.util.Log;
public class GetAccessToken {
	static InputStream is = null;
    static JSONObject jObj = null;
    static String json = "";
    public GetAccessToken() {
    }
    List<NameValuePair> params = new ArrayList<NameValuePair>();
    Map<String, String> mapn;
    DefaultHttpClient httpClient;
    HttpPost httpPost;
    public JSONObject gettoken(String address,String token,String client_id,String client_secret,String redirect_uri,String grant_type) {
        // Making HTTP request
        try {
            // DefaultHttpClient
            httpClient = new DefaultHttpClient();
            httpPost = new HttpPost(address);
            params.add(new BasicNameValuePair("code", token));
            params.add(new BasicNameValuePair("client_id", client_id));
            params.add(new BasicNameValuePair("client_secret", client_secret));
            params.add(new BasicNameValuePair("redirect_uri", redirect_uri));
            params.add(new BasicNameValuePair("grant_type", grant_type));
            httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded");
            httpPost.setEntity(new UrlEncodedFormEntity(params));
            HttpResponse httpResponse = httpClient.execute(httpPost);
            HttpEntity httpEntity = httpResponse.getEntity();
            is = httpEntity.getContent();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(
                    is, "iso-8859-1"), 8);
            StringBuilder sb = new StringBuilder();
            String line = null;
            while ((line = reader.readLine()) != null) {
                sb.append(line + "\n");
            }
            is.close();
            json = sb.toString();
            Log.e("JSONStr", json);
        } catch (Exception e) {
          e.getMessage();
            Log.e("Buffer Error", "Error converting result " + e.toString());
        }
        // Parse the String to a JSON Object
        try {
            jObj = new JSONObject(json);
        } catch (JSONException e) {
            Log.e("JSON Parser", "Error parsing data " + e.toString());
        }
        // Return JSON String
        return jObj;
    }
}

Create a class GoogleConstants. This class will hold all the constants required for Authentication and fetching Google Contacts.

package com.tusharwadekar.android.ttmm.util;

public class GoogleConstants {
	public static String CLIENT_ID = "";
	// Use your own client id

	public static String CLIENT_SECRET = "";
	// Use your own client secret

	public static String REDIRECT_URI = "http://localhost";
	public static String GRANT_TYPE = "authorization_code";
	public static String TOKEN_URL = "https://accounts.google.com/o/oauth2/token";
	public static String OAUTH_URL = "https://accounts.google.com/o/oauth2/auth";
	public static String OAUTH_SCOPE = "https://www.googleapis.com/auth/contacts.readonly";

	public static final String CONTACTS_URL = "https://www.google.com/m8/feeds/contacts/default/full";
	public static final int MAX_NB_CONTACTS = 1000;
	public static final String APP = "";
}

Now lets create an activity ImportGmailContactsActivity.

package com.tusharwadekar.android.ttmm;

import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONException;
import org.json.JSONObject;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.View.OnClickListener;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.Toast;

import com.google.gdata.client.contacts.ContactsService;
import com.google.gdata.data.contacts.ContactEntry;
import com.google.gdata.data.contacts.ContactFeed;
import com.google.gdata.data.extensions.Email;
import com.google.gdata.data.extensions.Name;
import com.tusharwadekar.android.ttmm.model.Contact;
import com.tusharwadekar.android.ttmm.oauth.GetAccessToken;
import com.tusharwadekar.android.ttmm.util.ConstantUtil;
import com.tusharwadekar.android.ttmm.util.GoogleConstants;

public class ImportGmailContactsActivity extends BaseActivity {
	final String TAG = getClass().getName();

	private Dialog auth_dialog;
	private ListView list;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_import_gmail_contacts);
		list = (ListView) findViewById(R.id.list);
		launchAuthDialog();
	}

	private void setContactList(List<Contact> contacts) {
		ArrayAdapter<Contact> adapter = new ArrayAdapter<Contact>(this,
				android.R.layout.simple_list_item_multiple_choice, contacts);
		list.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
		list.setAdapter(adapter);
	}

	private void launchAuthDialog() {
		final Context context = this;
		auth_dialog = new Dialog(context);
		auth_dialog.setTitle(getString(R.string.web_view_title));
		auth_dialog.setCancelable(true);
		auth_dialog.setContentView(R.layout.auth_dialog);

		auth_dialog.setOnCancelListener(new OnCancelListener() {
			@Override
			public void onCancel(DialogInterface dialog) {
				finish();
			}
		});

		WebView web = (WebView) auth_dialog.findViewById(R.id.webv);
		web.getSettings().setJavaScriptEnabled(true);
		web.loadUrl(GoogleConstants.OAUTH_URL + "?redirect_uri="
				+ GoogleConstants.REDIRECT_URI
				+ "&response_type=code&client_id=" + GoogleConstants.CLIENT_ID
				+ "&scope=" + GoogleConstants.OAUTH_SCOPE);
		web.setWebViewClient(new WebViewClient() {
			boolean authComplete = false;

			@Override
			public void onPageStarted(WebView view, String url, Bitmap favicon) {
				super.onPageStarted(view, url, favicon);
			}

			@Override
			public void onPageFinished(WebView view, String url) {
				super.onPageFinished(view, url);
				if (url.contains("?code=") && authComplete != true) {
					Uri uri = Uri.parse(url);
					String authCode = uri.getQueryParameter("code");
					authComplete = true;
					auth_dialog.dismiss();
					new GoogleAuthToken(context).execute(authCode);
				} else if (url.contains("error=access_denied")) {
					Log.i("", "ACCESS_DENIED_HERE");
					authComplete = true;
					auth_dialog.dismiss();
				}
			}
		});
		auth_dialog.show();
	}

	private class GoogleAuthToken extends AsyncTask<String, String, JSONObject> {
		private ProgressDialog pDialog;
		private Context context;

		public GoogleAuthToken(Context context) {
			this.context = context;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			pDialog = new ProgressDialog(context);
			pDialog.setMessage("Contacting Google ...");
			pDialog.setIndeterminate(false);
			pDialog.setCancelable(true);
			pDialog.setOnCancelListener(new OnCancelListener() {
				@Override
				public void onCancel(DialogInterface dialog) {
					finish();
				}
			});
			pDialog.show();
		}

		@Override
		protected JSONObject doInBackground(String... args) {
			String authCode = args[0];
			GetAccessToken jParser = new GetAccessToken();
			JSONObject json = jParser.gettoken(GoogleConstants.TOKEN_URL,
					authCode, GoogleConstants.CLIENT_ID,
					GoogleConstants.CLIENT_SECRET,
					GoogleConstants.REDIRECT_URI, GoogleConstants.GRANT_TYPE);
			return json;
		}

		@Override
		protected void onPostExecute(JSONObject json) {
			pDialog.dismiss();
			if (json != null) {
				try {
					String tok = json.getString("access_token");
					String expire = json.getString("expires_in");
					String refresh = json.getString("refresh_token");
					new GetGoogleContacts(context).execute(tok);
				} catch (JSONException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private class GetGoogleContacts extends
			AsyncTask<String, String, List<ContactEntry>> {

		private ProgressDialog pDialog;
		private Context context;

		public GetGoogleContacts(Context context) {
			this.context = context;
		}

		@Override
		protected void onPreExecute() {
			super.onPreExecute();
			pDialog = new ProgressDialog(context);
			pDialog.setMessage("Authenticated. Getting Google Contacts ...");
			pDialog.setIndeterminate(false);
			pDialog.setCancelable(true);
			pDialog.setOnCancelListener(new OnCancelListener() {
				@Override
				public void onCancel(DialogInterface dialog) {
					finish();
				}
			});
			pDialog.show();
		}

		@Override
		protected List<ContactEntry> doInBackground(String... args) {
			String accessToken = args[0];
			ContactsService contactsService = new ContactsService(
					GoogleConstants.APP);
			contactsService.setHeader("Authorization", "Bearer " + accessToken);
			contactsService.setHeader("GData-Version", "3.0");
			List<ContactEntry> contactEntries = null;
			try {
				URL feedUrl = new URL(GoogleConstants.CONTACTS_URL);
				ContactFeed resultFeed = contactsService.getFeed(feedUrl,
						ContactFeed.class);
				contactEntries = resultFeed.getEntries();
			} catch (Exception e) {
				pDialog.dismiss();
				Toast.makeText(context, "Failed to get Contacts",
						Toast.LENGTH_SHORT).show();
			}
			return contactEntries;
		}

		@Override
		protected void onPostExecute(List<ContactEntry> googleContacts) {
			if (null != googleContacts && googleContacts.size() > 0) {
				List<Contact> contacts = new ArrayList<Contact>();

				for (ContactEntry contactEntry : googleContacts) {
					String name = "";
					String email = "";

					if (contactEntry.hasName()) {
						Name tmpName = contactEntry.getName();
						if (tmpName.hasFullName()) {
							name = tmpName.getFullName().getValue();
						} else {
							if (tmpName.hasGivenName()) {
								name = tmpName.getGivenName().getValue();
								if (tmpName.getGivenName().hasYomi()) {
									name += " ("
											+ tmpName.getGivenName().getYomi()
											+ ")";
								}
								if (tmpName.hasFamilyName()) {
									name += tmpName.getFamilyName().getValue();
									if (tmpName.getFamilyName().hasYomi()) {
										name += " ("
												+ tmpName.getFamilyName()
														.getYomi() + ")";
									}
								}
							}
						}
					}
					List<Email> emails = contactEntry.getEmailAddresses();
					if (null != emails && emails.size() > 0) {
						Email tempEmail = (Email) emails.get(0);
						email = tempEmail.getAddress();
					}
					Contact contact = new Contact(name, email,
							ConstantUtil.CONTACT_TYPE_GOOGLE);
					contacts.add(contact);
				}
				setContactList(contacts);

			} else {
				Log.e(TAG, "No Contact Found.");
				Toast.makeText(context, "No Contact Found.", Toast.LENGTH_SHORT)
						.show();
			}
			pDialog.dismiss();
		}

	}

}

This activity does following tasks –
1) Once activity is create, launchAuthDialog() is called. This method is responsible to show Auth Dialog.
2) Have a look at loadUrl. Here we are passing following key information –
OAuth URL – https://accounts.google.com/o/oauth2/auth
Redirect URL – http://localhost
Client ID – Your Project ID which was created while  project setup.
Scope – Here we are using Contacts API so scope is – https://www.google.com/m8/feeds/contacts/default/full

web.loadUrl(GoogleConstants.OAUTH_URL + “?redirect_uri=”
+ GoogleConstants.REDIRECT_URI
+ “&response_type=code&client_id=” + GoogleConstants.CLIENT_ID
+ “&scope=” + GoogleConstants.OAUTH_SCOPE);

2) onPageFinished is called when user provides approval or denies the request.
3) If user provides approval, a special Auth Code is sent back from Google. We make a request to get Access Token from Google using this Auth Code. Here class GoogleAuthToken comes in picture.
Have a look at doInBackground method. Here we are making a request to get Access Token from Google using class GetAccessToken.
Token URL – https://accounts.google.com/o/oauth2/token
Auth Code – Which is received from Google when user approves permission in Google OAuth Dialog
Client ID – Your Project ID which was created in  project setup.
Client Secret – Your Project Secret which was created in  project setup.
Redirect URL – http://localhost
Grant Type – authorization_code

Once response from Google is received, a JSON object having access_token is received in onPostExecute callback.
4) Now that we have Access Token, we can make a request to get Contacts. Here class GetGoogleContacts comes in picture.
Have a look at doInBackground method, here we are creating an instance of ContactService. We are passing Access Token in Request Header.
Next we create instance of ContactFeed which will be used to get list of ContactEntry
Once response is received, onPostExecute method is called and list of ContactEntry is passed to the same. That’s it… each ContactEntry object will have Google contact details.

Apologies for late update but here is the Contact class as many have requested.

package com.tusharwadekar.android.ttmm.model;

public class Contact {

private int id;
private String name;
private String email;

public Contact(){}

public Contact(String name, String email) {
super();
this.name = name;
this.email = email;
}

public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}

@Override
public String toString() {
//return "Contact [id=" + id + ", name=" + name + ", email=" + email + "]";
return name;
}

}
Advertisements

About Tushar

Hi, I am Tushar Wadekar from Pune, India. I am an Adobe Certified Flash Developer. My Interests are: Flash, ActionScript, Flex, AIR, PHP, Ruby on Rails. You can reach me at: tusharwadekar[at]gmail[dot]com Thank you! Tushar
This entry was posted in Android, JAVA. Bookmark the permalink.

20 Responses to Android – Google Contact API 3.0 Example

  1. Mark Fuqua says:

    With your somewhat unique experience, have you considered creating an ANE for Adobe Air? I would purchase it. Either way…this has been bookmarked! Interesting read.

  2. Julian says:

    You share interesting things here. I think that your blog can go viral
    easily, but you must give it initial boost and i know how to do it, just search in google for – mundillo traffic
    increase go viral

  3. Vikram Viswanathan says:

    I tried using your code for learning to integrate Google Contacts API using OAuth. Unfortunately I’m getting an error on line 192 of ImportGmailContactsActivity class. Could you please guide me on this. I’m unable to move ahead in retrieving the contact list.

    Thanks in advance

  4. Lilly says:

    where is the class constant util and contact

  5. saqib says:

    Hii,
    Your code looks nice but i have some prob..

    you have extends BaseActivity here….BUt where is tha base activity..

    Please share the detail of base activty..

    — ContactEntry : class
    –Contact : class

    Please share detail about classes….i got error because class is not there..

  6. Avinash says:

    Hi, I tried using your code for learning to integrate Google Contacts API using OAuth. Unfortunately I’m getting an error in ImportGmailContactsActivity class. Could you please guide me on this. I’m unable to move ahead in retrieving the contact list.
    Please share these two files
    1.ContactEntry : class
    2.Contact : class

    Thanks in advance

  7. Isan Sahoo says:

    Need below two files:

    1.ContactEntry : class
    2.Contact : class

  8. Akhilesh says:

    For ContactService class why we need Guava Library to get imported? I am getting error in ExceptionInInitializationError

  9. compile ‘com.google.guava:guava:19.0-rc2’
    compile ‘com.google.gdata:core:1.47.1’

    for run well, Contact class you need to create by your own.

  10. Rutvik Macwan says:

    Need below two files:

    1.ContactEntry : class
    2.Contact : class

  11. I’m not using * as a metacharacter here, it was just being used as a replacement character so that my blog post could actually get through on the Williams network. THAT was the problem. Click http://link.mx/hool08200

  12. amasiaj2d says:

    So can You complet this class
    Contact please

  13. amasiaj2d says:

    And the ConstantUtil class please

  14. Tushar says:

    Hi All, added Contact class as many of you requested.

  15. amasiaj2d says:

    Thanks a lot Tushar! and I want to add, update and delete contact on google contacts how can I do this because I tried but not working!
    Thanks in advanced the answer

  16. Vishwas Soni says:

    Where is ConstantUtil and Base activity

  17. beni says:

    for android studio we need app graddle definition

  18. jay says:

    please write blog for Google Contact API 3.0 in iOS objective c

  19. Ankur Pandey says:

    403 error disallowed_useragent i am gettting this error in your android device

  20. Hello, the code is wrong formatted.. Please, can you check?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s