«

»

Nov 28

Reading contacts in Android 2.0+

On one of my Android apps, I added a trivial option to select a contact from the phone's contact list. This was working fine until SDK version 5, which changed the way the contacts are represented in the phone's SQLite database. It is using the "newer" class called ContactsContract instead of the deprecated People class

I have spent quite time in order to figure out which was the best way that will fit my needs. If you are in the same position, feel free to use the code below

My example below is will display a two row list of all the contacts, with alphabetical indexing. It is a mixture of code snippets I have found on the net, while may not be optimized; it will definitely give you the hang of things.

If you are only interested in the contact reading procedure, skip to the method named fillContactsList below, on ContactListDemo.java line nr. 28. This method demonstrates how contacts are accessed.

The rest of the projects also provides:

  1. Reading of all contacts and phone numbers with SDK5+ compliancy
  2. Displaying it in Two Row List Item
  3. Automatically created alphabetical Indexing with FastScrolling

This app must have android.permission.READ_CONTACTS in the AndroidManifest.xml

Here we go:

First, the data type Contact which will be used to store Name and Number:

Contact.java

public class Contact implements Comparable{

	private String Name;
 	private String number;

	public String getName() {
		return Name;
	}

	public void setName(String Name) {
		this.Name = Name;
	}

	public String getNumber() {
		return number;
	}

	public void setNumber(String number) {
		this.number = number;
	}

	public int compareTo(Object arg0) {
		Contact newCont = (Contact)arg0;
		return this.Name.compareTo(newCont.getName());
	}

}


Second: the adapter. This will be used to transfer the data to the ListActivity with indexing.

ContactArrayAdapter.java

public class ContactArrayAdapter extends ArrayAdapter<Contact> implements SectionIndexer{

	private final int resourceId;
	ArrayList<Contact> myElements;
	HashMap<String, Integer> alphaIndexer;
	String[] sections;

	@SuppressWarnings({ "unchecked" })
	public ContactArrayAdapter(Context context, int textViewResourceId, List objects) {
		super(context, textViewResourceId, objects);
		resourceId = textViewResourceId;
		myElements = (ArrayList<Contact>) objects;
		alphaIndexer = new HashMap<String, Integer>();
		int size = objects.size();
		for (int i = size - 1; i >= 0; i--) {
			Contact element = myElements.get(i);
			alphaIndexer.put(element.getName().substring(0, 1), i);

		}
		Set<String> keys = alphaIndexer.keySet();
		Iterator<String> it = keys.iterator();
		ArrayList<String> keyList = new ArrayList<String>();
		while (it.hasNext()) {
			String key = it.next();
			keyList.add(key);
		}
		Collections.sort(keyList);
		sections = new String[keyList.size()];
		keyList.toArray(sections);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {

		Contact c = (Contact) getItem(position);

		// if the array item is null, nothing to display, just return null
		if (c == null) {
			return null;
		}

		// We need the layoutinflater to pick up the view from xml
		LayoutInflater inflater = (LayoutInflater)
		getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);

		// Pick up the TwoLineListItem defined in the xml file
		TwoLineListItem view;
		if (convertView == null) {
			view = (TwoLineListItem) inflater.inflate(resourceId, parent, false);
		} else {
			view = (TwoLineListItem) convertView;
		}

		// Set value for the first text field
		if (view.getText1() != null) {
			view.getText1().setText(c.getName());
		}

		// set value for the second text field
		if (view.getText2() != null) {
			view.getText2().setText(c.getNumber());
		}

		return view;
	}

	public int getPositionForSection(int section) {
        String letter = sections[section];
        return alphaIndexer.get(letter);
	}

	public int getSectionForPosition(int position) {
		// TODO Auto-generated method stub
		return 0;
	}

	public Object[] getSections() {
		return sections;
	}

}

Third: the Layout. This will define how the name and number will be displayed:

listitemlayout.xml (should be under /res/layout):

<?xml version="1.0" encoding="utf-8"?>
<TwoLineListItem
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="wrap_content">

     <TextView android:id="@android:id/text1"
        android:layout_marginTop="1dip"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="15sp"
        android:textStyle="bold" />

    <TextView android:id="@android:id/text2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@android:id/text1"
        android:layout_alignLeft="@android:id/text1"
        android:paddingBottom="4dip"
        android:includeFontPadding="false"
        android:textSize="15sp"
        android:textStyle="normal" />

</TwoLineListItem>

And finally – the activity itself:

ContactListDemo.java

public class ContactListDemo extends ListActivity implements Runnable{

	private List<Contact> contacts = null;
	private Contact con;
	private ContactArrayAdapter cAdapter;
	private ProgressDialog prog = null;
	private Context thisContext = this;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		prog = ProgressDialog.show(this, "ContactListDemo", "Getting Contacts", true, false);
		Thread thread = new Thread(this);
		thread.start();

	}

	public void run() {
		if (contacts == null)
		{
			contacts = fillContactsList();

		}
		handler.sendEmptyMessage(0);
	}

	private List<Contact> fillContactsList() {
		List<Contact> tmpList = new ArrayList<Contact>();
		Cursor c = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);
		while(c.moveToNext()){
			String ContactID = c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
			String name = c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
			String hasPhone =c.getString(
					c.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER));
			if(Integer.parseInt(hasPhone) == 1){
				Cursor phoneCursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
						null,
						ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"='"+ContactID+"'",
						null, null);
				while(phoneCursor.moveToNext()){
					String number = phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
					con = new Contact();
					con.setName(name);
					con.setNumber(number);
					tmpList.add(con);
				}
				phoneCursor.close();
			}

		}
		c.close();
		Collections.sort(tmpList);
		return tmpList;
	}

	private Handler handler = new Handler() {
		@Override
		public void handleMessage(Message msg) {
			prog.dismiss();
			cAdapter = new ContactArrayAdapter(thisContext, R.layout.listitemlayout, contacts);
			getListView().setFastScrollEnabled(true);
			setListAdapter(cAdapter);

		}
	};

	@Override
	protected void onListItemClick(ListView l, View v, int position, long id) {
		super.onListItemClick(l, v, position, id);
		TextView label = ((TwoLineListItem) v).getText2();
		String phoneNumber = label.getText().toString();
		Toast.makeText(this, "Selected " + phoneNumber, Toast.LENGTH_SHORT).show();
	}
}

For your convenience, you can find the full project in our SVN repository

Hope this info was helpful.

Last note - I'm not giving credits to some websites I got the snippets from, just because it took so much time, and research to get this code working, and i don't remember which snipped belong to which website. If one of the owners/readers of a website is reading this... Give me a shout and I'll be glad to add credits

Share