» Программирование Android

Android Calendar API

В данном материале мы разберемся с использованием API для работы со стандартным календарем операционной системы Android. Данный API недокументирован и информации на данную тему не так много, так что надеемся, что эта статья прольет свет на некоторые полезные для разработчиков нюансы.

Зачем нужен данный API вообще? Причины могут быть разными, например – создание отдельного расписания. Или повторение событий не каждую неделю, а раз в две недели – чего нет в стандартном календаре. Тем не менее, если использовать стандартный API, то возможна синхронизация созданного приложения с Google Calendar. Также доступны встроенные виджеты календаря, сторонние виджеты для работы с ним же – в общем, работа именно со стандартным Android-календарем несомненно удобна.

Однако есть некоторые нюансы. Если использовать «правильный» API Google Calendar, то автоматически накладываются некоторые ограничения: невозможность использования без соединения с Интернетом, необходимость постоянной синхронизации, при составлении расписания данные за весь год идут до сервера, а при синхронизации – обратно – происходит удвоение используемого интернет-трафика. Поэтому несколько удобнее использовать офлайн версию стандартного календаря – и синхронизировать её только тогда, когда это удобно пользователю. Компания Google не опубликовала официального API, но благодаря «хорошим людям» получилось найти необходимые ContentProviders.

Что интересно – самого API вроде бы и нет, а разрешения для него документированы. Соответственно, их придется добавить в AndroidManifest.xml:

            <uses-permission android:name="android.permission.READ_CALENDAR"> </uses-permission>

<uses-permission android:name="android.permission.WRITE_CALENDAR"> </uses-permission>

URI – это Universal Resource Identificator, то есть унифицированный идентификатор ресурса. Чтобы было понятнее – для использования календаря Android, нужно обратиться к его ContentProvider по определенному URI. Данный URI разный в разных версиях операционной системы. Однако это решаемо. Для Android 2.2 Froyo и последующих версий практически всегда это: content://com.android.calendar/calendars. Для версий, «младше» 2.2 — content://calendar/calendars.

То есть, грубо говоря, можно определять приложением версию системы, после чего использовать нужный URI. Однако мы поступим несколько иначе. Произведем проверку, есть ли что-то по URI для версий ниже 2.2, и если нет, то используем оставшийся URI.

// Calendar URI before 2.2

Uri calendars = Uri.parse("content://calendar/calendars");

Cursor managedCursor = this.managedQuery(calendars, new String[] { "_id", "name" }, null, null, null);

if(managedCursor == null || managedCursor.getCount() == 0)

{

    // 2.2 (Froyo)

}

else

{

    // < 2.2

}

Вот мы и получили этот самый URI. Теперь нам нужно получить список календарей. Что это значит – Google Calendar на самом деле состоит из одного или нескольких небольших календарей, которые могут различаться цветом маркеров. Зачастую имеется в виду что-то вроде «учеба», «работа», «личный» и так далее. Такие календари создавать можно только на сайте этого календаря – в самом устройстве такой функционал не предусмотрен.

Для того, чтобы добавить события, нужно знать ID календаря. Используем конструкцию, которую применяли в предыдущем пункте:

            String[] projection = new String[] { "_id", "name" };

Cursor managedCursor = this.managedQuery(URI, projection, null, null, null);

if (managedCursor != null && managedCursor.moveToFirst())

        {

            String calName;

            String calID;              

            int nameColumn = managedCursor.getColumnIndex("name");

            int idColumn = managedCursor.getColumnIndex("_id");

            do

            {

                        calName = managedCursor.getString(nameColumn);

                        calID = managedCursor.getString(idColumn);

                        if (calName != null) // … UI                                      

           } while (managedCursor.moveToNext());

            managedCursor.close();

        }

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

Что касается добавления событий – работа с календарем аналогична работе с любым другим ContentProvider.

            ContentValues event = new ContentValues();

event.put("calendar_id", calID);  // ID календаря мы получили ранее

event.put("title", title); // Название события

event.put("description", desc); // Описание события

event.put("eventLocation", loc); // Место проведения

event.put("dtstart", start); // время начала

event.put("dtend", end); // время окончания

eventsURI=Uri.parse(URI + “/events");

this.getContentResolver().insert(eventsURI, event);

Рассмотрим поля события. Поле описания лучше использовать для индикации события. При использовании данного поля, можно удалять сразу несколько событий, которые были созданы написанным нами приложением. Поля названия, описания, а также места – строковые. Не забываем про поля времени, они имеют тип long, поэтому их значения могут быть сгенерированы, например, вот такой функцией:

public static long pickDate(int year, int month, int day, int hour, int minute)

{

            Calendar rightNow = Calendar.getInstance();

            int timeShift = rightNow.get(Calendar.ZONE_OFFSET);            

            return Date.UTC(year - 1900, month, day, hour - (timeShift / 3600000), minute, 0);

}

Как видно, в данном коде происходит учет временной зоны.

Для того, чтобы удалить сообщение с использованием ID, нужно добавить его к URI.

            //eventsURI = URI + events/

uri = ContentUris.withAppendedId(eventsURI, EVENT_ID);

this.getContentResolver().delete(uri, null, null);

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

Важно – если неправильно написать URI, то гаджет, скорее всего, начнет выдавать ошибку при синхронизации. Более того – испытывать свою работу придется на настоящих физических устройствах, так как в эмуляторе AVD нет календаря.