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:
<font face="Courier New">ContactsContract.Contacts.CONTENT_URI</font>
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:
<code><uses-permission android:name="android.permission.READ_CONTACTS"/>
</code>
The following code creates a tabular layout to display contacts:
<code><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>
</code>
We retrieve the URI for accessing the contacts using the following command:
<code>Uri contacts=ContactsContract.Contacts.CONTENT_URI;</code>
Next, we create a <font face="Courier New">CursorLoader</font>
object to load all the contacts in the ascending order of contact names as follows:
<code>CursorLoader loader=new CursorLoader(this,contacts,null,null,null,ContactsContract.Contacts.DISPLAY_NAME+" asc");</code>
The <font face="Courier New">CursorLoader</font>
constructor takes the following parameters:
<font face="Courier New">Context</font>
context <font face="Courier New">Uri</font>
uri <font face="Courier New">String[]</font>
projection <font face="Courier New">String</font>
selection <font face="Courier New">String[]</font>
selectionArgs <font face="Courier New">String</font>
sortOrder
The following code populates a string array with contact names:
<code>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++;
}
</code>
In the above code, the contacts are loaded in a <font face="Courier New">Cursor</font>
object using the <font face="Courier New">loadInBackground()</font>
method of the <font face="Courier New">CursorLoader</font>
class. All contact names are stored in a string array by navigating through all contacts using the <font face="Courier New">moveToNext()</font>
method of the <font face="Courier New">Cursor</font>
class.
Thereafter an <font face="Courier New">ArrayAdapter</font>
is used to bind the contact names to an <font face="Courier New">AutoCompleteTextView</font>
as follows:
<code>ArrayAdapter<string> adapter=new ArrayAdapter<string>(this,android.R.layout.simple_dropdown_item_1line,names);
txtSearchVal.setThreshold(1);
txtSearchVal.setAdapter(adapter);
c.moveToFirst();
showContact(c);
</string></string></code>
In the above code an <font face="Courier New">AutoCompleteTextView</font>
called txtSearchVal is used to display a list of suggested contact names as a user types a contact name. The <font face="Courier New">setThreshold()</font>
method of the <font face="Courier New">AutoCompleteTextView</font>
class is used to specify minimum number of characters that must be typed before the suggestion list is displayed and the <font face="Courier New">setAdapter()</font>
method is used to bind the <font face="Courier New">ArrayAdapter</font>
to the <font face="Courier New">AutoCompleteTextView</font>
. Then it navigates to the first record using the <font face="Courier New">moveToFirst()</font>
method of the <font face="Courier New">Cursor</font>
class and displays the details of the first contact using the user defined <font face="Courier New">showContact()</font>
method.
The <font face="Courier New">showContact()</font>
method is written to display a contact as follows:
<code>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();
}
</code>
The above code retrieves the id, display name, photo and phone numbers of a contact using the cursor parameter. It uses the <font face="Courier New">openContactPhotoInputStream()</font>
method to return an input stream for the photo and the <font face="Courier New">decodeStream()</font>
method to read the photo. Then it uses the <font face="Courier New">setImageBitmap()</font>
method to display the contact photo on the <font face="Courier New">ImageView</font>
. 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:
<code>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);
}
}
</code>
Clicking on the Search button allows you to search a contact on the basis of the name entered in the Search textbox as follows:
<code>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);
}
</code>
The above code displays the contact details if the contact name is found.
Clicking the Clear Search textbox executes the following code:
<code>public void clearSearch(View View)
{
showAll();
txtSearchVal.setText("");
}
</code>
The <font face="Courier New">showAll()</font>
method displays all contacts as follows:
<code>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);
}
</code>
The following code allows navigation using the navigation buttons:
<code>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);
}
</code>
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.