понедельник, 31 марта 2014 г.

Работаем со списком вызовов

Для того чтобы получить доступ к списку вызовов необходимо воспользоваться контент-провайдером и получить доступ к таблице «CallLog.Calls». Код написан для API 10, т.е. будет работать на версиях Android от 2.3.3 и выше.

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



В этой таблице хранится информация обо всех вызовах. Для примера, я создал приложение, которое получает информацию обо всех вызовах. Информация включает в себя: Имя вызова (контакта), номер, тип вызова, дату вызова, длительность. О доступной информации, подробно можно почитать в документации http://developer.android.com/reference/android/provider/CallLog.Calls.html#constants.
Сортировка в таблице по умолчанию (столбец «DEFAULT_SORT_ORDER») производится по дате вызова.

О чём нам необходимо помнить

  • Номер вызова (столбец «NUMBER») может не быть в нашей телефонной книге, а значит, не будет имени (столбец «CACHED_NAME») для вызова. Пишем условие, проверяющее этот случай. Если имени нет, то выводим вместо него номер.
  • Тип звонка (столбец «TYPE»), из таблицы возвращается как строка, но на самом деле там число, значит, не забываем перевести в Integer. Также пишем оператор выбора «case», чтобы перевести число в текстовое значение.
  • Длительность разговора (столбец «DURATION»), тут значение возвращается в виде строки, но само значение является числом (INTEGER (long)), которое отображает длительность разговора в секундах. Т.е. нам нужно перевести секунды в минуты, желательно в формате «04:53».
  • Дата вызова (столбец «DATE»), возвращается значение в виде строки, но само значение является числом (INTEGER (long)), которое отображает дату вызова в миллисекундах с начала эпохи. Опять же необходимо перевести это число в формат «31.03.2014 16:07». Не забываем, что время в миллисекундах и в Unix – системе («January 1, 1970» вместо «12:00 A.M. on December 30, 1899»), о функциях работы со временем и датами можно почитать в официальной документации http://docwiki.embarcadero.com/RADStudio/XE5/en/Date_and_Time_Support

Теперь напишем код (Delphi XE5 Update 2):
uses
  FMX.Helpers.Android, Androidapi.JNI.GraphicsContentViewText,
  Androidapi.JNI.Provider, Androidapi.JNI.JavaTypes, System.DateUtils;

procedure TForm1.Button1Click(Sender: TObject);
var
  cursor: JCursor;
  CallTypeInt: Integer;
  CallDuration, CallDate: Int64;
  CallName, CallTypeStr: String;
  ListBoxItem: TListBoxItem;
begin

  cursor := SharedActivity.getContentResolver.query(
   TJCallLog_Calls.JavaClass.CONTENT_URI,
    nil,
     nil,
      nil,
       nil);

  ListBox1.Clear;
  ListBox1.BeginUpdate;

  if (cursor.getCount > 0) then
  begin

    while (cursor.moveToNext) do
    begin

      CallName := JStringToString(cursor.getString(
        cursor.getColumnIndex(TJCallLog_Calls.JavaClass.CACHED_NAME)));

      if CallName = '' then begin
        CallName := JStringToString(cursor.getString(
        cursor.getColumnIndex(TJCallLog_Calls.JavaClass.NUMBER)));
      end;

      CallTypeInt := StrToInt(JStringToString(cursor.getString(
        cursor.getColumnIndex(TJCallLog_Calls.JavaClass.&TYPE))));

      case CallTypeInt of
        1: CallTypeStr := 'Входящие';
        2: CallTypeStr := 'Исходящие';
        3: CallTypeStr := 'Пропущенные';
        else CallTypeStr := 'Отменено';
      end;

      CallDuration := StrToInt64(JStringToString(cursor.getString(
        cursor.getColumnIndex(TJCallLog_Calls.JavaClass.DURATION))));

      CallDate := StrToInt64(JStringToString(cursor.getString(
        cursor.getColumnIndex(TJCallLog_Calls.JavaClass.DATE))));

      ListBoxItem := TListBoxItem.Create(ListBox1);

      ListBoxItem.ItemData.Text := CallTypeStr + ': ' + CallName;

      ListBoxItem.ItemData.Detail := DateTimeToStr(TTimeZone.Local.ToLocalTime(
        UnixToDateTime(CallDate div 1000))) + '   Длительность: '
         + Format('%2.2d:%2.2d', [CallDuration div 60, CallDuration mod 60]);

      ListBox1.AddObject(ListBoxItem);

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

    end;
  end;

  cursor.close;

  ListBox1.EndUpdate;

end;

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

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

Скриншоты:


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

Спасибо за внимание.

p.s. Надеюсь, вам пригодится данный материал.
p.s.2. Если вы заметили ошибку, то сообщите мне.

5 комментариев:

  1. возможно вопрос не по теме, но можно ли из приложения сделать телефонный вызов? Чтобы пользователю, который нажмет в приложении на номер телефона или какую-то кнопку, выдалось сообщение:"набрать номер?" и происходил набор номера.

    ОтветитьУдалить
    Ответы
    1. Можно, используйте интенты, вот хорошая статья с примерами: http://www.webdelphi.ru/2014/01/delphi-xe5-ispolzovanie-intent-namereniya-v-android/

      Удалить
  2. подскажите пожалуйста, каким образом работать с смс-сообщениями? В Androidapi.JNI.Provider не нашел интерфейса http://developer.android.com/reference/android/provider/Telephony.Sms.html

    ОтветитьУдалить
    Ответы
    1. JNI в Delphi из коробки, написано для API 10. То что вы ищете, появилось только в API 19.

      Удалить
  3. А как сделать мониторинг вызова, а именно узнать идёт вызов или нет, что бы сделать определённые функции вот с это сайта но не пойму как реализовать в делфи xe7 http://m.habrahabr.ru/post/162181/

    ОтветитьУдалить