Статьи

Контакты API 2.0 и выше | Учебник для разработчиков Android

Начиная с Android 2.0 (уровень API 5), платформа Android предоставляет улучшенный API контактов для управления и интеграции контактов из нескольких учетных записей и из других источников данных. Для обработки перекрывающихся данных из нескольких источников поставщик контента контактов объединяет похожие контакты и представляет их пользователям как единый объект. В этой статье описывается, как использовать новый API для управления (вставки, обновления, удаления, просмотра) контактов.

Новый API Контактов определен в android.provider.ContactsContract и связанных классах. Более старый API все еще поддерживается, хотя и устарел.

Нам нужно понять основную структуру хранилища, чтобы лучше манипулировать контактами. У нас есть три различных типа таблиц — контакты, необработанные контакты и данные.

Все данные, относящиеся к контакту, хранятся в этой общей таблице данных, где каждая строка сообщает, какие данные он хранит через свой тип MIME. Таким образом, мы могли бы иметь Phone.CONTENT_ITEM_TYPE в качестве MIME-типа строки данных, он содержит данные Phone. Аналогично, если в качестве типа MIME строки у нас есть Email.CONTENT_ITEM_TYPE, то в нем хранятся данные электронной почты. Как и эта серия строк данных связана с одним необработанным контактом.

Каждый необработанный контакт относится к данным конкретного контакта, поступающим из одного единственного источника — скажем, вашей учетной записи Gmail или вашей офисной учетной записи Microsoft.

Контакт является самым верхним в иерархии, которая объединяет похожие данные из разных источников в один контакт — очень удобная функция, когда у вас есть избыточные данные, поступающие об одном и том же контакте из разных учетных записей — например, учетная запись Facebook, учетная запись orkut, учетная запись Yahoo. и таращить глаза Итак, иерархия выглядит следующим образом:
 
 
 
 
 
поэтому, когда мы хотим вставить новый контакт, мы всегда вставляем необработанный контакт. Когда мы хотим обновить существующий контакт, мы чаще всего имеем дело с таблицами данных, которые доступны через серию классов CommonDataKind. Потому что это будет для обновления определенных типов данных, таких как телефон или электронная почта.
Переходя к примеру:

я создаю упражнение с четырьмя кнопками для просмотра, добавления, изменения и удаления контактов.
 

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");
}
});

При нажатии на каждую из кнопок я вызываю их соответствующие методы: например, displayContacts (), createContact (), updatecContact () и deleteContact (). Теперь мы увидим каждый из этих методов.

 

 

 

displayContacts () довольно прост. Доступ к каждой из упомянутых выше таблиц осуществляется через URI контента. Я использую URI верхнего уровня «Контакты», чтобы перебрать все существующие контакты и отобразить их имена и номера телефонов (Toast их).

Мы знаем, что Контакты — это ContentProvider, и, следовательно, нам нужно выполнить запрос через ContentResolver, который возвращает все данные контактов.

 

  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();
}
}
}
}

Я постараюсь кратко объяснить вышеописанный метод. Строка 1 получает дескриптор ContentResolver. Строка 2 запрашивает URI контактов (ContactsContract.Contacts.CONTENT_URI) без какого-либо упоминания конкретных столбцов или предложения «где» в SQL-запросе. Обратите внимание, что все 4 параметра являются нулевыми. Это означает, что мы не делаем никаких условных запросов в таблицу контактов и, следовательно, все данные возвращаются в курсор.

Затем я проверяю, что курсор не пустой, и перебираю данные курсора. Я получаю _ID и DISPLAY_NAME из контактов, а затем проверяю флаг, если у контакта есть номер телефона. Эта информация доступна в самой таблице контактов. Но детали телефонного номера находятся в таблицах данных. Поэтому после проверки флага я запрашиваю у CommonDataKings.Phone.CONTENT_URI данные телефона с этим конкретным идентификатором. Из этого нового курсора с именем pCur я получаю номер телефона. Если для одного контакта существует несколько телефонных номеров, все они будут извлечены и переведены в тосты один за другим.

Теперь давайте посмотрим, как создать или вставить новый контакт.

В методе createContact (), который вызывается при нажатии на кнопку «Добавить контакт», я сначала запрашиваю, существует ли уже жестко закодированное имя «Имя образца». Если это так, я провожу тост в сообщении о том же Если нет, то я действительно вставляю имя вместе с номером телефона. Первую часть проверки вы можете просмотреть в полном исходном коде, доступном для скачивания. Только вторая часть вставки контакта объясняется здесь. Для этого нам нужно всегда использовать ContentResolver.

ContentResolver предоставляет метод applyBatch (…), который принимает массив классов ContentProviderOperation в качестве параметра. Все данные, встроенные в ContentProviderOperations, фиксируются или вставляются в ContentProvider, над которым мы работаем. В этом случае ContentProvider, над которым мы работаем, это Contacts, а полномочия, связанные с ним, это ContactsContract.AUTHORITY.

Вот код для того же:

        ArrayList<ContentProviderOperation> ops = new ArrayList<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 = new

ArrayList<ContentProviderOperation>();

        ops.add(ContentProviderOperation.newDelete(ContactsContract.RawContacts.CONTENT_URI)
.withSelection(where, params)
.build());
try {
cr.applyBatch(ContactsContract.AUTHORITY, ops);
} catch (RemoteException e) {
….

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.