A searchable Contact List app in Android






4.65/5 (6 votes)
This article is a demonstration of a simple searchable contacts list in Android.
Introduction
Through this article, I wish to explain how to create a searchable "Contact List" android app. Using this app, a user can navigate through all the stored contacts using navigation buttons and search for a contact on the basis of the Contact Name. The app can also display the Contact Photo if available.
To navigate through the contacts the user can use the <<, <, > and >> buttons.
To search for a contact the user has to type the contact name in the "Search Name" text box and click the "Search" button. Clicking the "Clear Search" button clears the "Search Name" text box and displays the last viewed contact before starting the search.
Background
Contacts in Android are managed using a content provider. A content provider is a kind of data store, which can be queried as well as edited like a database. The way it stores its data is irrelevant to the application which uses it. However it is important to know how to access the data in order to use it.
Contacts can be queried from the contacts table by using the following URI:
ContactsContract.Contacts.CONTENT_URI
Using the code
Since this app reads contacts from the device, the following entry is required in the AndroidManifest.xml file to allow permission to the app to read contacts:
<uses-permission android:name="android.permission.READ_CONTACTS"/>
The following code creates a tabular layout to display contacts:
<TableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="350dp">
<TableRow>
<TextView android:id="@+id/txtId"
android:width="175dp"
android:text="Contact Id: "/>
<TextView android:id="@+id/txtIdVal"
android:width="175dp"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/txtDisplayName"
android:width="175dp"
android:text="Contact Name: "/>
<TextView android:id="@+id/txtDisplayNameVal"
android:width="175dp"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/txtPhoneNo"
android:width="175dp"
android:text="Phone Number: "/>
<TextView android:id="@+id/txtPhoneNoVal"
android:width="175dp"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/txtPhoto"
android:width="175dp"
android:text="Photo: "/>
<ImageView android:id="@+id/imgPhoto"
android:width="175dp"/>
</TableRow>
<TableRow>
<Button android:id="@+id/btnFirst"
android:width="175dp"
android:text="<<"
android:onClick="first"/>
<Button android:id="@+id/btnPrevious"
android:width="175dp"
android:text="<"
android:onClick="previous"/>
</TableRow>
<TableRow>
<Button android:id="@+id/btnNext"
android:width="175dp"
android:text=">"
android:onClick="next"/>
<Button android:id="@+id/btnLast"
android:width="175dp"
android:text=">>"
android:onClick="last"/>
</TableRow>
<TableRow>
<TextView android:id="@+id/txtSearch"
android:width="175dp"
android:text="Search Name: "/>
<AutoCompleteTextView android:id="@+id/txtSearchVal"
android:width="175dp"/>
</TableRow>
<TableRow>
<Button android:id="@+id/btnSearch"
android:width="175dp"
android:text="Search"
android:onClick="search"/>
<Button android:id="@+id/btnClearSearch"
android:width="175dp"
android:text="Clear Search"
android:onClick="clearSearch"/>
</TableRow>
</TableLayout>
We retrieve the URI for accessing the contacts using the following command:
Uri contacts=ContactsContract.Contacts.CONTENT_URI;
Next, we create a CursorLoader
object to load all the contacts in the ascending order of contact names as follows:
CursorLoader loader=new CursorLoader(this,contacts,null,null,null,ContactsContract.Contacts.DISPLAY_NAME+" asc");
The CursorLoader
constructor takes the following parameters:
Context
contextUri
uriString[]
projectionString
selectionString[]
selectionArgsString
sortOrder
The following code populates a string array with contact names:
c=loader.loadInBackground();
names=new String[c.getCount()];
int ctr=0;
while(c.moveToNext())
{
names[ctr]=c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
ctr++;
}
In the above code, the contacts are loaded in a Cursor
object using the loadInBackground()
method of the CursorLoader
class. All contact names are stored in a string array by navigating through all contacts using the moveToNext()
method of the Cursor
class.
Thereafter an ArrayAdapter
is used to bind the contact names to an AutoCompleteTextView
as follows:
ArrayAdapter adapter=new ArrayAdapter(this,android.R.layout.simple_dropdown_item_1line,names);
txtSearchVal.setThreshold(1);
txtSearchVal.setAdapter(adapter);
c.moveToFirst();
showContact(c);
In the above code an AutoCompleteTextView
called txtSearchVal is used to display a list of suggested contact names as a user types a contact name. The setThreshold()
method of the AutoCompleteTextView
class is used to specify minimum number of characters that must be typed before the suggestion list is displayed and the setAdapter()
method is used to bind the ArrayAdapter
to the AutoCompleteTextView
. Then it navigates to the first record using the moveToFirst()
method of the Cursor
class and displays the details of the first contact using the user defined showContact()
method.
The showContact()
method is written to display a contact as follows:
public void showContact(Cursor c)
{
String id=c.getString(c.getColumnIndex(ContactsContract.Contacts._ID));
String displayName=c.getString(c.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
Bitmap photo;
InputStream stream=ContactsContract.Contacts.openContactPhotoInputStream
(getContentResolver(),ContentUris.withAppendedId(ContactsContract.Contacts.CONTENT_URI,Long.parseLong(id)));
if(stream!=null)
{
photo=BitmapFactory.decodeStream(stream);
imgPhoto.setImageBitmap(photo);
}
else
{
imgPhoto.setImageBitmap(null);
}
Cursor phoneCursor=getContentResolver().query
(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,null,ContactsContract.CommonDataKinds.Phone.CONTACT_ID+"="+id,null,null);
String number="";
if(phoneCursor.getCount()>0)
{
phoneCursor.moveToFirst();
number=phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
while(phoneCursor.moveToNext())
{
number+=","+phoneCursor.getString(phoneCursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
}
}
phoneCursor.close();
txtIdVal.setText(id);
txtDisplayNameVal.setText(displayName);
txtPhoneNoVal.setText(number);
enableDisableButtons();
}
The above code retrieves the id, display name, photo and phone numbers of a contact using the cursor parameter. It uses the openContactPhotoInputStream()
method to return an input stream for the photo and the decodeStream()
method to read the photo. Then it uses the setImageBitmap()
method to display the contact photo on the ImageView
. To display the phone numbers, we have to use another query as the information is stored in another table.
The following code enables and disables the navigation buttons based on the query results:
public void enableDisableButtons()
{
if(c.isFirst()&&c.isLast())
{
btnFirst.setEnabled(false);
btnPrevious.setEnabled(false);
btnNext.setEnabled(false);
btnLast.setEnabled(false);
}
else if(c.isFirst())
{
btnFirst.setEnabled(false);
btnPrevious.setEnabled(false);
btnNext.setEnabled(true);
btnLast.setEnabled(true);
}
else if(c.isLast())
{
btnFirst.setEnabled(true);
btnPrevious.setEnabled(true);
btnNext.setEnabled(false);
btnLast.setEnabled(false);
}
else
{
btnFirst.setEnabled(true);
btnPrevious.setEnabled(true);
btnNext.setEnabled(true);
btnLast.setEnabled(true);
}
}
Clicking on the Search button allows you to search a contact on the basis of the name entered in the Search textbox as follows:
public void search(View v)
{
position=c.getPosition();
if(txtSearchVal.getText().toString().trim().length()>0)
{
Uri contacts=ContactsContract.Contacts.CONTENT_URI;
CursorLoader loader=new CursorLoader
(this,contacts,null,ContactsContract.Contacts.DISPLAY_NAME+"='"+txtSearchVal.getText().toString()+"'",null,
ContactsContract.Contacts.DISPLAY_NAME+" asc");
c=loader.loadInBackground();
if(c.getCount()>0)
{
c.moveToFirst();
}
}
else
{
Uri contacts=ContactsContract.Contacts.CONTENT_URI;
CursorLoader loader=new CursorLoader
(this,contacts,null,null,null,ContactsContract.Contacts.DISPLAY_NAME+" asc");
c=loader.loadInBackground();
c.move(position);
c.moveToNext();
}
if(c.getCount()==0)
{
Toast.makeText(this,"No results found for contact "+txtSearchVal.getText().toString(),Toast.LENGTH_SHORT).show();
showAll();
return;
}
showContact(c);
}
The above code displays the contact details if the contact name is found.
Clicking the Clear Search textbox executes the following code:
public void clearSearch(View View)
{
showAll();
txtSearchVal.setText("");
}
The showAll()
method displays all contacts as follows:
public void showAll()
{
Uri contacts=ContactsContract.Contacts.CONTENT_URI;
CursorLoader loader=new CursorLoader(this,contacts,null,null,null,ContactsContract.Contacts.DISPLAY_NAME+" asc");
c=loader.loadInBackground();
c.move(position);
c.moveToNext();
showContact(c);
}
The following code allows navigation using the navigation buttons:
public void first(View v)
{
c.moveToFirst();
showContact(c);
}
public void previous(View v)
{
c.moveToPrevious();
showContact(c);
}
public void next(View v)
{
c.moveToNext();
showContact(c);
}
public void last(View v)
{
c.moveToLast();
showContact(c);
}
Points of Interest
In this article, I have attempted to demonstrate working with the Contacts content provider in a simple manner. I hope that readers will find it useful.