Insert, Update, Delete, View contacts in android
2.0
Starting from Android 2.0 (API Level 5), the Android
platform provides an improved Contacts API for managing and integrating
contacts from multiple accounts and from other data sources. To handle overlapping
data from multiple sources, the contacts content provider aggregates similar
contacts and presents them to users as a single entity. This article describes
how to use the new API to manage (insert, update, delete, view) contacts.
안드로이드 2.0버전부터 주소록을 관리하고 다른 데이터와 통합시키는 기능이 향상된 주소록API가 제공되었다. 다양한 데이터를 추상화시키기 위해 주소록CP는 비슷한 주소록을 모아 사용자에게 하나의 정보로 제공하는데 어떻게 하면 이 새로운 API를 가지고 주소록을 관리할 수 있는지 살펴보도록 하자.
The new Contacts API is defined in the android.provider.ContactsContract and
related classes. The older API is still supported, although deprecated.
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.
주소록을 좀 더 효과적으로 조작하기 위해서는 주소록의 구조에 대해 이해할 필요가 있다.
주소록은 크게 3가지 테이블을 가진다.
All data related to a contact is stored in this 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.
Each Raw Contact refers to a specific contact’s data coming from one single source –
say, you gmail account or your office Microsoft account.
The Contact is the
topmost in the hierarchy which aggregates similar looking data from various
sources into one single contact – a very handy feature when you have redundant data
coming about the same contact from you various accounts – like a facebook
account, orkut account, yahoo account and goggle account. So the
hierarchy looks like this:
So, when we want to insert a new contact, we always
insert a Raw Contact. When we want to update an existing contact, we most often
deal with the data tables which
are accessible through a series of CommonDataKind classes. Because this would be to update
particular types of data like phone or email.
Coming to the example:
I create an activity with four buttons to View, Add,
Modify and Delete Contacts.
Button view = (Button)findViewById(R.id.viewButton);
Button add = (Button)findViewById(R.id.createButton);
Button modify = (Button)findViewById(R.id.updateButton);
Button delete = (Button)findViewById(R.id.deleteButton);
view.setOnClickListener(new OnClickListener() {
public void onClick(View
v){
displayContacts();
Log.i("NativeContentProvider", "Completed
Displaying Contact list");
}
});
On the click of each of the buttons I invoke their
respective methods: like displayContacts(),createContact(), updatecContact() and deleteContact(). We will now see each of these methods.
displayContacts() is pretty straightforward. Access to each of the
tables mentioned above is through a content URI. I use the topmost level ‘Contacts’ URI to
iterate through all the existing contacts and display their names and phone
numbers (Toast them).
We know Contacts is a ContentProvider and hence we need to query through a ContentResolver which
returns all the data of the contacts.
private void displayContacts() {
ContentResolver cr
= getContentResolver();
Cursor cur =
cr.query(ContactsContract.Contacts.CONTENT_URI,
null, null, null, null);
if (cur.getCount()
> 0) {
while (cur.moveToNext()) {
String id =
cur.getString(cur.getColumnIndex(ContactsContract.Contacts._ID));
String name =
cur.getString(cur.getColumnIndex(ContactsContract.Contacts.DISPLAY_NAME));
if (Integer.parseInt(cur.getString(
cur.getColumnIndex(ContactsContract.Contacts.HAS_PHONE_NUMBER)))
> 0) {
Cursor pCur = cr.query(
ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
null,
ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
new String[]{id}, null);
while (pCur.moveToNext())
{
String phoneNo = pCur.getString(pCur.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
Toast.makeText(NativeContentProvider.this, "Name:
" +
name + ", Phone No: " + phoneNo, Toast.LENGTH_SHORT).show();
}
pCur.close();
}
}
}
}
I will try to briefly explain the above method.
Line 1 gets a handle to the ContentResolver. Line 2 queries the Contacts URI (ContactsContract.Contacts.CONTENT_URI ) without
any mention of the specific columns or “where” clause of an SQL query. Notice
all the 4 parameters are null. This means that we are not making any
conditional query into the contacts table and hence all data is returned into
the cursor.
Next, I check that
the cursor is not empty and iterate through the cursor data. I retrieve
the _ID and
DISPLAY_NAME from the
Contacts and then, I check for the flag if the contact has a phone number. This
information is available in contacts table itself. But the Phone number details
are in the data tables. Hence after checking for the flag, I query the CommonDataKings.Phone.CONTENT_URI for the phone data of that specific ID. From this new
cursor named pCur,
I retrieve the Phone Number. If there are multiple phone number for one
contact, all of them will be retrieved and toasted one after another.
Now, let us see how to create or insert a new contact.
In the createContact() method which is called when you click on the “Add
Contact” button, I first query to see if the hardcoded name “Sample Name”
already exists. If so, I toast a message stating the same. If not, then I get
into actually inserting the name along with a phone number. The first part of
the check you can view in the complete source code available for download. Only the second part of inserting a
contact is explained here. For this, we need to use a ContentResolver always.
A ContentResolver provides an applyBatch(…) method which takes an array of ContentProviderOperation classes as
a parameter. All the data built into the ContentProviderOperations are
committed or inserted into the ContentProvider that we are working on. IN this case, the ContentProvider we are working on are Contacts and the authority
associated with the same is ContactsContract.AUTHORITY
Here is the code for the same:
ArrayList<ContentProviderOperation> ops = newArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE,"accountname@gmail.com")
.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, "com.google")
.build());
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name)
.build());
ops.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
.withValue(ContactsContract.Data.MIMETYPE,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
.withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, phone)
.withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_HOME)
.build());
try {
cr.applyBatch(ContactsContract.AUTHORITY, ops);
….
In the first element in the ops array, I am setting the details of
the account to which I want to add the contact. In this case the gmail account
type is accontname@gmail.com and the
account name is“com.google”. The latter has to be unique and hence it is
recommended to use the internet URLs of the source of data.
In the second element,
I am adding the name of the contact and in the third the phone data. You notice that
I use .withValueBackReference(..) as we
still have not created the Raw contact and hence we do not have the Id. The
first row creates the id and hands over the id to the next rows of data.
This array ops is
passed into the ContentResolver and thus the data is inserted.
For updating the phone number of an existing contact, I
again use the ContentResolver with the ContentProviderOperation array.
However, now, I pass the where clause and the parameters of the where clause –
specifically indicating that only the phone number of the “Sample Name” contact
has to be updated.
ops.add(ContentProviderOperation.newUpdate(ContactsContract.Data.CONTENT_URI)
.withSelection(where, params)
.withValue(ContactsContract.CommonDataKinds.Phone.DATA, phone)
.build());
Notice the .withSelection(where, params). The where and
params look like this:
String where = ContactsContract.Data.DISPLAY_NAME + " = ? AND " +
ContactsContract.Data.MIMETYPE + " = ? AND " +
String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE) + " = ?
";
String[] params = new String[] {name,
ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
String.valueOf(ContactsContract.CommonDataKinds.Phone.TYPE_HOME)};
Delete too is done in a very similar manner with no
values but only the selection criteria is provided for which contact to be
deleted.
ContentResolver cr = getContentResolver();
String where = ContactsContract.Data.DISPLAY_NAME + " = ? ";
String[] params = new String[] {name};
ArrayList<ContentProviderOperation> ops = newArrayList<ContentProviderOperation>();
ops.add(ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI)
.withSelection(where,
params)
.build());
try {
cr.applyBatch(ContactsContract.AUTHORITY, ops);
} catch (RemoteException e) {
….
The complete source
code is downloadable here.
I really scouted the internet a lot to find a
comprehensive tutorial on the new Contacts Content Provider API but could not
find anything that easily. Hope this helps all who of you who are looking for
the same.
'안드로이드 > ADB & Rooting' 카테고리의 다른 글
Root Your Samsung Galaxy S2: Here’s How! (0) | 2011.08.14 |
---|---|
이미지에 포함 할 product 설정하기 (0) | 2011.08.14 |
[KERNEL] adb "cannot run as root in production builds" fix (0) | 2011.08.14 |
How To Use ADB: All Commands and Options (0) | 2011.08.14 |
How to Install BusyBox on Rooted Android Phone (0) | 2011.08.14 |