Reading Android's Call Log

By Paulus, 2 October, 2013

I'm currently working on an application that allows a person to block phone numbers whether it be an actual phone call or a SMS/MMS message. One of the features of this app is the ability to retrieve all the incoming and outgoing calls then compiling a list. From that list a user can select a number and black list it. 

Prerequisites

In order to read the call log, you must specify the android.permission.READ_CONTACTS in your AndroidManifest.xml file:

<uses-permission android:name="android.permission.READ_CONTACTS" />

Code

I'm creating an ArrayList that takes a custom data type called PhoneNumber. This data type is fairly simple as it only contains two private variables for the number and the person's name. The person's name will be available only if they exist as a contact in the phone.

public static ArrayList<PhoneNumber> getCalls(Context context) {
	ArrayList<PhoneNumber> list = new ArrayList<PhoneNumber>();
	Cursor cursor;
	// The fields we want to select from the internal database. Setting this
	// to null is equivalent to * (e.g., SELECT * FROM...)
	String[] projection = {CallLog.Calls.NUMBER, CallLog.Calls.DATE};
	String sortOrder = CallLog.Calls.DATE + " desc";
	int numberCol = 0;
	String contactName;
	String contactNumber;
	PhoneNumber phone_number;

	// Query the CallLog table, selecting only the number and date then sorting it by the date.
	cursor = context.getContentResolver().query(CallLog.Calls.CONTENT_URI, projection, null, null, sortOrder);

	numberCol = cursor.getColumnIndex(CallLog.Calls.NUMBER);

	if(cursor.moveToFirst()) {
		while(cursor.moveToNext()) {

			contactName = getContactName(context, cursor.getString(numberCol)); 
			contactNumber = cursor.getString(numberCol);
			phone_number = new PhoneNumber(contactName, contactNumber); 

			if(!list.contains(phone_number)) {
				list.add(phone_number);
			}
		}
	}

	cursor.close();

	return list;
}

Line 6 is used to define what columns we want. It is possible to define projection as:

String[] projection = {"number", "date"};

Using CallLog.Calls.NUMBER and CallLog.Calls.DATE ensures that if the column names ever change in the future, you won't have to update the app. See CallLog.Calls for a complete list of fields.

We declare numberCol on line 8 and assign it a value on line 16. This variable holds the index of the number column from the query. On line 21 the function getContactName is a custom function as shown below.

public static String getContactName(Context context, String sNumber) {
	Uri uri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI, Uri.encode(sNumber));

	String[] projection = {ContactsContract.Contacts.DISPLAY_NAME};
	String name;

	Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);

	if(cursor.moveToFirst()) {

		name = cursor.getString(cursor.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));

	} else {

		name = "";

	}

	cursor.close();

	return name;
}

The getContactName is a pretty straight forward function. The only thing worth mentioning is what happens on line 2. We need to append the number of the person we're looking for. The rest of the function is pretty similar to the getCalls function. If you would like to know what fields are available in the contacts database, see ContactsContract.Contacts.