Android Contacts

Introduction

In this blog post I will do a quick android phone contacts crash course. After reading this you will be able to: query all contacts from the phone and query additional commonly used information about a specific contact.

First I will present some real world situations when we need to retrieve the phone contacts, after that I will present the android architecture for storing contact information and finally I will give you the exact details to do all you want with the address book in code.

Real World Usage

Either you will want to develop a contact manager app that will heavily use android contacts provider, or if you just go for a social app that has to use address book contacts, it’s always good to know you way around contacts in an android mobile application.

Android Contact Structure

We need to understand the underlying structure of storage to better manipulate the contacts. We have three distinct types of tables – Contacts, Raw Contacts and Data.

The contacts content provider (android.provider.ContactsContract) aggregates similar contacts and presents them to users as a single entity.

The Contact is the topmost in the hierarchy which aggregates Raw Contacts.

Each Raw Contact refers to a specific contact’s data coming from one single source – say, your gmail account, your yahoo account or your Microsoft Live account.

All data related to a Raw Contact is stored in a generic data table with each row telling what is the data it stores through its MIME type. So we could have a Phone.CONTENT_ITEM_TYPE as the MIME type of the data row, it contains Phone data. Similarly, if we have Email.CONTENT_ITEM_TYPE as the row’s MIME type, then it stores email data. Like this lot of data rows are associated with a single Raw Contact.

Android Contact API

Granting Access

Before an application can query the contact records access must be granted through the AndroidManifest.xml file stored in the root of the project. Add the following uses-permission belows the uses-sdk statement.

1
uses-permission android:name="android.permission.READ_CONTACTS"

Querying The Android Contact Database

Retrieving Contact List

First we will retrieve basic contact information stored in Contacts table. The focus is to query the columns containing the following Id:

ContactsContract.Contacts._ID 
ContactsContract.Contacts.DISPLAY_NAME
ContactsContract.Contacts.HAS_PHONE_NUMBER
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    public Cursor GetContacts()
    {
        ContentResolver cr = localContext.getContentResolver();
 
        try
        {
            Uri uri = ContactsContract.Contacts.CONTENT_URI;
 
            String[] projection = new String[] {
                    ContactsContract.Contacts._ID,
                    ContactsContract.Contacts.DISPLAY_NAME,
                    ContactsContract.Contacts.HAS_PHONE_NUMBER
            };
 
            String where = ContactsContract.Contacts.IN_VISIBLE_GROUP + " = '1'";
 
            String[] selectionArgs = null;
 
            String sortOrder = ContactsContract.Contacts.DISPLAY_NAME;
 
            return cr.query(uri, projection, where, selectionArgs, sortOrder);
        }
        catch (Exception ex)
        {
        String message = ex.getMessage();
        Log.e(TAG, "Error: " + message, ex);
 
        return null;
        }
    }

Retrieving Contact Details

After having all the contacts we can go on and find out more data about each contact. I will give examples for phone number, email, birthday and also photo.

I will use the ContactId field to filter after only one record, including a where clause to limit the returned data.

Phone Number

Phone numbers are stored in their own table and need to be queried separately. To query the phone number table use the URI stored in this SDK variable:

ContactsContract.CommonDataKinds.Phone.CONTENT_URI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
    public Cursor GetContactPhone(int contactId)
    {
        ContentResolver cr = localContext.getContentResolver();
 
        try
        {
            Uri uri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
 
            String[] projection = new String[] {
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID,
                    ContactsContract.CommonDataKinds.Phone.NUMBER,
                    ContactsContract.Data.MIMETYPE
             };
 
            String where = ContactsContract.Data.CONTACT_ID + "=?"
                    + " AND " + ContactsContract.Data.MIMETYPE + "=?";
 
            // Add contactId filter.
            String[] selectionArgs = new String[] { 
                    String.valueOf(contactId),
                    ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE
            };
 
            String sortOrder = null;
 
            return cr.query(uri, projection, where, selectionArgs, sortOrder);
        }
        catch (Exception ex)
        {
        String message = ex.getMessage();
        Log.d(TAG, "Error: " + message);
 
        return null;
        }
    }

Since multiple phone numbers can be stored loop through the records returned in the Cursor.

Email

Emails are also stored in their own table and need to be queried separately. To query the email address table use the URI stored in this SDK variable:

ContactsContract.CommonDataKinds.Email.CONTENT_URI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public Cursor GetContactEmail(int contactId)
    {
        ContentResolver cr = localContext.getContentResolver();
 
        try
        {
            Uri uri = ContactsContract.CommonDataKinds.Email.CONTENT_URI;
 
            String[] projection = new String[] {
                    ContactsContract.CommonDataKinds.Email.CONTACT_ID,
                    ContactsContract.CommonDataKinds.Email.DATA,
                    ContactsContract.Data.MIMETYPE
             };
 
            String where = ContactsContract.Data.CONTACT_ID + "=?"
                    + " AND " + ContactsContract.Data.MIMETYPE + "=?";
 
            // Add contactId filter.
            String[] selectionArgs = new String[] { 
                    String.valueOf(contactId),
                    ContactsContract.CommonDataKinds.Email.CONTENT_ITEM_TYPE
            };
 
            String sortOrder = null;
 
            return cr.query(uri, projection, where, selectionArgs, sortOrder);
        }
        catch (Exception ex)
        {
        String message = ex.getMessage();
        Log.d(TAG, "Error: " + message);
            return null;
        }
    }

Since multiple email addresses can be stored loop through the records returned in the Cursor.

Birthday

Retrieving the Birthday is a bit more challenging as I have to use a MimeType to access the contacts date of birth:

ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    public Cursor GetContactBirthday(int contactId)
    {
        ContentResolver cr = localContext.getContentResolver();
 
        try
        {
            Uri uri = ContactsContract.Data.CONTENT_URI;
 
            String[] projection = new String[] {
                    ContactsContract.Data.CONTACT_ID,
                    ContactsContract.CommonDataKinds.Event.START_DATE,
                    ContactsContract.Data.MIMETYPE,
                    ContactsContract.CommonDataKinds.Event.TYPE
             };
 
            String where = ContactsContract.Data.CONTACT_ID + "=?"
                    + " AND " + ContactsContract.Data.MIMETYPE + "=?"
                    + " AND " + ContactsContract.CommonDataKinds.Event.TYPE + "=?";
 
            // Add contactId filter.
            String[] selectionArgs = new String[] { 
                    String.valueOf(contactId),
                    ContactsContract.CommonDataKinds.Event.CONTENT_ITEM_TYPE,
                    String.valueOf(ContactsContract.CommonDataKinds.Event.TYPE_BIRTHDAY)
            };
 
            String sortOrder = null;
 
            return cr.query(uri, projection, where, selectionArgs, sortOrder);
        }
        catch (Exception ex)
        {
        String message = ex.getMessage();
        Log.d(TAG, "Error: " + message);
 
        return null;
        }
    }
Photo

The contact photos are stored as binary large objects (blob) in the Data table. We can directly get the input stream for the contact photo as shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    final public Bitmap GetContactPhoto(android.content.Context context)
    {
        ContentResolver cr = context.getContentResolver();
 
        Uri uri = ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI, ContactId);
 
        InputStream input = ContactsContract.Contacts.openContactPhotoInputStream(cr, uri);
 
        if (input == null) {
            return null;
        }
 
        return BitmapFactory.decodeStream(input);
    }
Other Data

Lots of other data can be stored for each contact, so depending on what are you after you could find. All of this data can be retrieved using this URI:

ContactsContract.Data.CONTENT_URI

This,and of course using different MIMETYPE in the where clause.

ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE //postal addreses
ContactsContract.CommonDataKinds.Im.CONTENT_ITEM_TYPE //instant messenger
ContactsContract.CommonDataKinds.Organization.CONTENT_ITEM_TYPE //organizations
ContactsContract.CommonDataKinds.Note.CONTENT_ITEM_TYPE //notes

Summary and Look-out

The presented ways are implemented to show a clear and easy understandable way to access information about contacts, not for the best performance ( which will depend heavily on your application architecture and goals ).

Other information such as the address, instant messenger, notes, etc. can easily be queried analogous to the way shown above. For further data fields just look at the classes in the following package:

android.provider.ContactsContract.CommonDataKinds

You may also like...

7 Responses

  1. ken says:

    Hello

    can you teach me how to add birthday?

    i have try for many days

    thanks,

    • Hi Ken,
      You can find the code for getting the birthday date for a phone contact above.
      I will also add a zip file with the source code for the project, so you can download it and use it.

      If you want to add a birthday to a phone contact ( not just read it ), that’s another thing, so please let me know and I will try to help you.

  2. Sher says:

    The article is useful. Thanks Great work.
    Can you please draw the internal structure diagram of these tables? Its difficult to understand.

  3. Farhan Ahmad says:

    yeah nice info, will be much helpful, if you can show test data in tables. like a table for contacts,raw contacts and data.
    Also, try to add a full new contact with as much fields as possible.

  4. jeike says:

    what you done is great thank U. but I have a problem, I don’t how to put Bitmap in my ImageView. I use ListFragment

  5. Jeike says:

    what’s contactId?? cuz I tried to replace it by ContactsContract.Contacts._ID but it’s not work. I want helping

  6. Lucky says:

    Hi! This works well. Can you tell how to store these contacts to excel sheet?

Leave a Reply

Your email address will not be published. Required fields are marked *