Важно (9.07.22)

Если картинки в постах не отображаются, зайдите в блог через прокси. РКН заблокировал поддомены blogger.com на которые загружались картинки.

воскресенье, 30 марта 2014 г.

Работаем со списком контактов, #2

Эта статья является продолжением первой части (Работаем со списком контактов, #1). В этой статье вы узнаете, как определять тип контакта и как определить/вывести тип номера в виде строки.

Upd (20.04.14). Проверил код на Delphi XE6 и добавил информацию о необходимых изменениях.


Для определения типа контакта (или тип аккаунта, которому принадлежит контакт), нам понадобится таблица «raw_contacts», а конкретно столбец «ACCOUNT_TYPE».
В этом столбце хранится тип аккаунта, к которому принадлежит контакт и в паре со столбцом «ACCOUNT_NAME» идентифицирует конкретный аккаунт.

К сожалению, единственный тип аккаунта одинаковый на всех устройствах это аккаунт Google, его значение «com.google». Остальные типы (SIM и Телефон), на всех устройствах пишутся по-разному. В этом виноваты производители Android устройств!


Я провёл немало тестов и пытался найти универсальное решение для всех устройств, но так и не нашёл его. Единственный вариант, добавить условие проверки для всех возможных типов. Если вы знаете, как решить данную проблему, то обязательно напишите мне.

Вот информация (ACCOUNT_TYPE) по устройствам, которые мне удалось заполучить:
Аккаунт Google:
  • Все устройства - «com.google» 
SIM-карта:
  • Samsung Galaxy S Plus, Samsung Galaxy S2 - «vnd.sec.contact.sim»
  • HTC Sensation Z710e, HTC Rhyme – «com.anddroid.contacts.sim»
  • Sony Xperia go – «нет информации»
Контакты телефона:
  • Samsung Galaxy S Plus, Samsung Galaxy S2 - «vnd.sec.contact.phone»
  • HTC Sensation Z710e, HTC Rhyme – «com.htc.android.pcsc»
  • Sony Xperia go – «com.sonyericsson.localcontacts»
Теперь необходимо составить условие для выборки контактов.
Например, нам нужны контакты, сохранённые в телефоне:  " AND (ACCOUNT_TYPE = 'vnd.sec.contact.phone' OR ACCOUNT_TYPE = 'com.htc.android.pcsc' OR ACCOUNT_TYPE = 'com.sonyericsson.localcontacts')".

Как будет выглядеть результат:


Код для кнопки:
procedure TForm1.Button1Click(Sender: TObject);
var
  cursor, cursor2: JCursor;
  id: integer;
  ListBoxItem: TListBoxItem;
begin

  cursor := SharedActivity.getContentResolver.query(
   TJContactsContract_Contacts.JavaClass.CONTENT_URI,
    nil,
     StringToJString('HAS_PHONE_NUMBER = 1'),
      nil,
       StringToJString('DISPLAY_NAME ASC'));

  ListBox1.Clear;
  ListBox1.BeginUpdate;

  if (cursor.getCount > 0) then
  begin

    while (cursor.moveToNext) do
    begin

      id := cursor.getInt(cursor.getColumnIndex(StringToJString('_ID')));

      cursor2 := SharedActivity.getContentResolver.query(
       TJContactsContract_RawContacts.JavaClass.CONTENT_URI,
        nil,
         StringToJString('CONTACT_ID = ' + IntToStr(id) + ' AND ' +
         '(ACCOUNT_TYPE = ' + '''vnd.sec.contact.phone''' +
         ' OR ACCOUNT_TYPE = ' + '''com.htc.android.pcsc''' +
         ' OR ACCOUNT_TYPE = ' + '''com.sonyericsson.localcontacts'')'),
          nil,
           nil);

      while (cursor2.moveToNext) do
      begin
        ListBoxItem := TListBoxItem.Create(ListBox1);

        ListBoxItem.ItemData.Text := JStringToString(cursor.getString(
          cursor.getColumnIndex(StringToJString('DISPLAY_NAME'))));

        ListBoxItem.ItemData.Detail := JStringToString(cursor2.getString(
          cursor2.getColumnIndex(StringToJString('ACCOUNT_TYPE'))));

        ListBoxItem.Tag := id;
        ListBox1.AddObject(ListBoxItem);
      end;

      cursor2.close;

      Label1.Text := 'Всего: ' + IntToStr(ListBox1.Count);

    end;
  end;

  cursor.close;

  ListBox1.EndUpdate;

end;

Далее, научим наше приложение выводить тип для всех номеров отдельно взятого контакта.

В предыдущей статье, мы с вами уже научились выводить идентификатор типа, а значит половина дела сделано. Нам остаётся только найти текстовое значение для каждого идентификатор типа.
Для этого пишем вот такую строчку кода:
ListBoxItem.ItemData.Detail := JStringToString(SharedActivity.getString(
          TJCommonDataKinds_Phone.JavaClass.getTypeLabelResource(NumberType)));

Также следует не забывать, что пользователь может создать свой собственный тип номера. Этот тип будет храниться в столбце «DATA3», а значение идентификатора будет равно «0». Значит, нужно написать условие проверяющее значение идентификатора на ноль.

Код для события «OnItemClick»:
procedure TForm1.ListBox1ItemClick(const Sender: TCustomListBox;
  const Item: TListBoxItem);
var
  id, NumberType: integer;
  FIO: string;
  cursor: JCursor;
  ListBoxItem : TListBoxItem;
begin

  TabControl1.ActiveTab := TabItem2;

  id := Item.Tag;
  FIO := Item.Text;

  Label2.Text := FIO;

  cursor := SharedActivity.getContentResolver.query(
   TJCommonDataKinds_Phone.JavaClass.CONTENT_URI,
    nil,
     StringToJString('CONTACT_ID = ' + IntToStr(id)),
      nil,
       nil);

  ListBox2.Clear;
  ListBox2.BeginUpdate;

  if(cursor.getCount > 0) then
  begin

    while (cursor.moveToNext) do
    begin

      ListBoxItem := TListBoxItem.Create(ListBox2);

      ListBoxItem.ItemData.Text := JStringToString(cursor.getString(
        cursor.getColumnIndex(TJCommonDataKinds_Phone.JavaClass.NUMBER)));

      NumberType := cursor.getInt(cursor.getColumnIndex(StringToJString('DATA2')));

      if NumberType = 0 then
      begin
        ListBoxItem.ItemData.Detail := JStringToString(cursor.getString(
          cursor.getColumnIndex(StringToJString('DATA3'))));
      end
      else
      begin
        ListBoxItem.ItemData.Detail := JStringToString(SharedActivity.getString(
          TJCommonDataKinds_Phone.JavaClass.getTypeLabelResource(NumberType)));
      end;

      ListBox2.AddObject(ListBoxItem);

    end;
  end;

  cursor.close;

  ListBox2.EndUpdate;

end;

UPDATE (20.04.14):
Чтобы код заработал в Delphi XE6, необходимо:
в "uses" заменить модуль "Androidapi.JNI.JavaTypes" на "Androidapi.Helpers".

Архив обновлён (Добавил комментарий для Delphi XE6)!

Как выглядит результат:


Исходный код: Скачать с Google Drive

На этом всё.
Удачной разработки.