lightbulb_outline Help shape the future of the Google Play Console, Android Studio, and Firebase. Start survey

Поставщик календаря

Поставщик календаря представляет собой репозиторий для событий календаря пользователя. API поставщика календаря позволяет запрашивать, вставлять, обновлять и удалять календари, события, участников, напоминания и т. д.

API поставщика календаря может использоваться как приложениями, так и адаптерами синхронизации. Правила зависят от типа программы, которая выполняет вызовы. В этой статье главным образом рассматривается использование API поставщика календаря в качестве приложения. Сведения о различиях между адаптерами синхронизации представлены в разделе Адаптеры синхронизации.

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

Основы

Поставщики контента хранят в себе данные и предоставляют к ним доступ для приложений. Поставщики контента, предлагаемые платформой Android (включая поставщик календаря) обычно представляют данные в виде набора таблиц, в основе которых лежит модель реляционной базы данных. Каждая строка в такой таблице представляет собой запись, а каждый столбец — данные определенного типа и значения. Благодаря API поставщика календаря приложения и адаптеры синхронизации получают доступ на чтение/запись к таблицам в базе данных, в которых представлены данные календаря пользователя.

Каждый поставщик календаря предоставляет общедоступный URI (упакованный в объект Uri), который служит уникальным идентификатором своего набора данных. Поставщик контента, который управляет несколькими наборами данных (несколькими таблицами), предоставляет отдельный URI для каждого набора. Все URI поставщиков начинаются со строки content://. Она определяет данные, которые находятся под управлением поставщика контента. Поставщик календаря задает константы для URI каждого из своих классов (таблиц). Такие URI имеют формат <class>.CONTENT_URI. Например, Events.CONTENT_URI.

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

Calendar Provider Data Model

Рисунок 1. Модель данных поставщика календаря.

У пользователя может быть несколько календарей, причем они могут быть связаны с аккаунтами разных типов (Google Календарь, Exchange и т. д.).

Класс CalendarContract определяет модель данных календаря и информацию, относящуюся к событиям. Эти данные хранятся в различных таблицах, указанных ниже.

Таблица (класс) Описание

CalendarContract.Calendars

В этой таблице находится информация о календарях. В каждой строке этой таблицы представлены сведения об отдельном календаре, например, его название, цвет, информация о синхронизации и т. д.
CalendarContract.Events В этой таблице находится информация о событиях. В каждой строке этой таблицы содержится информация об отдельном событии —например, заголовок события, место проведения, время начала, время завершения и т. д. Событие может быть однократным или повторяющимся. Сведения об участниках, напоминаниях и расширенные свойства хранятся в отдельных таблицах. В каждой из них имеется целочисленная переменная EVENT_ID, которая ссылается на объект _ID в таблице событий.
CalendarContract.Instances В этой таблице содержатся данные о времени начала и окончания каждого повторения события. В каждой строке этой таблицы представлено одно повторение события. Однократные события сопоставляются с повторениями один к одному. Для повторяющихся событий автоматически создаются несколько строк, которые соответствуют нескольким повторениям события.
CalendarContract.Attendees В этой таблице находится информация об участниках (гостях). В каждой строке этой таблицы указан один гость. В ней указываются тип гостя и информация о том, посетит ли он событие.
CalendarContract.Reminders В этой таблице находятся данные уведомлений или оповещений. В каждой строке этой таблицы указано одно уведомление или оповещение. Для одного события можно создать несколько напоминаний. Максимальное количество таких напоминаний для события задается с помощью целочисленной переменной MAX_REMINDERS, значение которой задает адаптер синхронизации, владеющий указанным календарем. Напоминания задаются в минутах до начала события и имеют метод, который определяет порядок уведомления пользователя.

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

  • Вставка, обновление и просмотр событий календаря. Чтобы вставить, изменить и считать события напрямую из поставщика календаря, требуются соответствующие разрешения. Однако, если вы не планируете создавать полнофункциональное приложение календаря или адаптер синхронизации, запрашивать такие разрешения не обязательно. Вместо этого можно использовать намерения, поддерживаемые приложением «Календарь» Android, для обработки операций чтения и записи в этом приложении. При использовании намерений ваше приложение отправляет пользователям приложение «Календарь» для выполнения требуемой операции в предварительно заполненной форме. По завершении они возвращаются в приложение. Реализовав в вашем приложении возможность выполнения часто используемых операций через приложение «Календарь», вы обеспечиваете для пользователей единообразный и функциональный пользовательский интерфейс. Мы рекомендуем использовать именно такой подход. Дополнительные сведения представлены в разделе Намерения календаря.

  • Адаптеры синхронизации. Адаптер синхронизации синхронизирует данные календаря на устройстве пользователя с данными на сервере или в другом источнике данных. В таблицах CalendarContract.Calendars и CalendarContract.Events имеются столбцы, зарезервированные для адаптеров синхронизации. Ни поставщик, ни приложения не должны изменять их. Фактически они скрыты до тех пор, пока адаптер синхронизации не начнет использовать их. Дополнительные сведения об адаптерах синхронизации представлены в разделе Адаптеры синхронизации.

Разрешения пользователей

Чтобы считать данные календаря, в файл манифеста приложения необходимо включить разрешение READ_CALENDAR. Также в него следует включить разрешение WRITE_CALENDAR для удаления, вставки или обновления данных календаря:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"...>
    <uses-sdk android:minSdkVersion="14" />
    <uses-permission android:name="android.permission.READ_CALENDAR" />
    <uses-permission android:name="android.permission.WRITE_CALENDAR" />
    ...
</manifest>

Таблица календарей

В таблице CalendarContract.Calendars содержатся подробные сведения о каждом отдельном календаре. Выполнять запись в указанные ниже столбцы этой таблицы могут и приложение, и адаптер синхронизации. Полный список поддерживаемых полей представлен в справке по классу CalendarContract.Calendars.

Константа Описание
NAME Название календаря.
CALENDAR_DISPLAY_NAME Название этого календаря, которое отображается для пользователя.
VISIBLE Логическое значение, обозначающее, выбран ли календарь для отображения. Значение «0» указывает на то, что события, связанные с этим календарем, не отображаются. Значение «1» указывает на то, что события, связанные с этим календарем, отображаются. Это значение влияет на создание строк в таблице CalendarContract.Instances.
SYNC_EVENTS Логическое значение, обозначающее, следует ли синхронизировать календарь и хранить имеющиеся в нем события на устройстве. Значение «0» указывает, что не следует синхронизировать этот календарь или хранить имеющиеся в нем события на устройстве. Значение «1» указывает, что этот календарь следует синхронизировать и хранить имеющиеся в нем события на устройстве.

Запрос календаря

Ниже представлен пример того, как получить календари, которыми владеет определенный пользователь. Для простоты демонстрации операция запроса в этом примере находится в потоке пользовательского интерфейса («основной поток»). На практике это следует делать в асинхронном потоке, а не в основном. Дополнительные сведения представлены в статье Загрузчики. Если же вы не только считываете данные, но и вносите в них изменения, обратитесь к справке по классу AsyncQueryHandler.

// Projection array. Creating indices for this array instead of doing
// dynamic lookups improves performance.
public static final String[] EVENT_PROJECTION = new String[] {
    Calendars._ID,                           // 0
    Calendars.ACCOUNT_NAME,                  // 1
    Calendars.CALENDAR_DISPLAY_NAME,         // 2
    Calendars.OWNER_ACCOUNT                  // 3
};

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_ACCOUNT_NAME_INDEX = 1;
private static final int PROJECTION_DISPLAY_NAME_INDEX = 2;
private static final int PROJECTION_OWNER_ACCOUNT_INDEX = 3;

В следующей части примера создается запрос. С помощью выбора определяются критерии для запроса. В этом примере выполняется поиск календарей со следующими значениями параметров: ACCOUNT_NAME — sampleuser@google.com, ACCOUNT_TYPE — com.google и OWNER_ACCOUNT  — sampleuser@google.com. Для просмотра всех просмотренных пользователем календарей, а не только имеющихся у него, не указывайте параметр OWNER_ACCOUNT. Запрос возвращает объект Cursor, который можно использовать для перебора результатов, возвращенных запросом к базе данных. Дополнительные сведения об использовании запросов в поставщиках контента представлены в статье Поставщики контента.

// Run query
Cursor cur = null;
ContentResolver cr = getContentResolver();
Uri uri = Calendars.CONTENT_URI;
String selection = "((" + Calendars.ACCOUNT_NAME + " = ?) AND ("
                        + Calendars.ACCOUNT_TYPE + " = ?) AND ("
                        + Calendars.OWNER_ACCOUNT + " = ?))";
String[] selectionArgs = new String[] {"sampleuser@gmail.com", "com.google",
        "sampleuser@gmail.com"};
// Submit the query and get a Cursor object back.
cur = cr.query(uri, EVENT_PROJECTION, selection, selectionArgs, null);

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

// Use the cursor to step through the returned records
while (cur.moveToNext()) {
    long calID = 0;
    String displayName = null;
    String accountName = null;
    String ownerName = null;

    // Get the field values
    calID = cur.getLong(PROJECTION_ID_INDEX);
    displayName = cur.getString(PROJECTION_DISPLAY_NAME_INDEX);
    accountName = cur.getString(PROJECTION_ACCOUNT_NAME_INDEX);
    ownerName = cur.getString(PROJECTION_OWNER_ACCOUNT_INDEX);

    // Do something with the values...

   ...
}

Изменение календаря

Чтобы обновить календарь, можно указать _ID календаря: либо в виде идентификатора, добавленного к URI (withAppendedId()), либо в качестве первого элемента выделения. Выделение должно начинаться с "_id=?", а первым аргументом selectionArg должен быть _ID календаря. Также для выполнения обновлений можно закодировать идентификатор в URI. В этом примере для изменения отображаемого имени календаря используется подход (withAppendedId()):

private static final String DEBUG_TAG = "MyActivity";
...
long calID = 2;
ContentValues values = new ContentValues();
// The new display name for the calendar
values.put(Calendars.CALENDAR_DISPLAY_NAME, "Trevor's Calendar");
Uri updateUri = ContentUris.withAppendedId(Calendars.CONTENT_URI, calID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);

Вставка календаря

Для управления календарями в основном используются адаптеры синхронизации, поэтому новые календари следует вставлять исключительно как адаптер синхронизации. По большей части приложения могут вносить в календари только поверхностные изменения, такие как изменение отображаемого имени. Если приложению требуется создать локальный календарь, это можно сделать путем вставки календаря в виде адаптера синхронизации с помощью параметра ACCOUNT_TYPE типа ACCOUNT_TYPE_LOCAL. ACCOUNT_TYPE_LOCAL представляет собой особый тип аккаунтов для календарей, которые не связаны с аккаунтом устройства. Календари этого типа не синхронизируются с сервером. Дополнительные сведения об адаптерах синхронизации представлены в статье Адаптеры синхронизации.

Таблица событий

В таблице CalendarContract.Events содержатся подробные сведения о каждом отдельном событии. Чтобы получить возможность добавлять, обновлять или удалять события, в файл манифеста приложения необходимо включить разрешение WRITE_CALENDAR.

Выполнять запись в указанные ниже столбцы этой таблицы могут и приложение, и адаптер синхронизации. Полный список поддерживаемых полей представлен в справке по классу CalendarContract.Events.

Константа Описание
CALENDAR_ID _ID календаря, к которому принадлежит событие.
ORGANIZER Адрес эл. почты организатора (владельца) события.
TITLE Название события.
EVENT_LOCATION Место проведения.
DESCRIPTION Описание события.
DTSTART Время начала события по UTC (в миллисекундах) от точки отсчета.
DTEND Время окончания события по UTC (в миллисекундах) от точки отсчета.
EVENT_TIMEZONE Часовой пояс события.
EVENT_END_TIMEZONE Часовой пояс для времени окончания события.
DURATION Продолжительность события в формате RFC5545. Например, значение "PT1H" обозначает, что событие должно длиться один час, а значение "P2W" указывает на продолжительность в 2 недели.
ALL_DAY Значение «1» обозначает, что это событие занимает весь день по местному часовому поясу. Значение «0» указывает на то, что это регулярное событие, которое может начаться и завершиться в любое время в течение дня.
RRULE Правило повторения для формата события. Например, "FREQ=WEEKLY;COUNT=10;WKST=SU". С другими примерами можно ознакомиться здесь.
RDATE Даты повторения события. Обычно RDATE используется вместе с RRULE для определения агрегированного набора повторяющихся событий. Дополнительные сведения представлены в спецификации RFC5545.
AVAILABILITY Если событие считается как занятое или как свободное время, доступное для планирования.
GUESTS_CAN_MODIFY Указывает, могут ли гости вносить изменения в событие.
GUESTS_CAN_INVITE_OTHERS Указывает, могут ли гости приглашать других гостей.
GUESTS_CAN_SEE_GUESTS Указывает, могут ли гости просматривать список участников.

Добавление событий

Когда ваше приложение вставляет новое событие, мы рекомендуем использовать намерение INSERT, как описано в разделе Использование намерения для вставки события. Однако при необходимости вы можете вставлять события напрямую. В этом разделе как раз описывается то, как это сделать.

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

  • Необходимо указать CALENDAR_ID и DTSTART.
  • Необходимо указать EVENT_TIMEZONE. Чтобы получить список установленных в системе идентификаторов часовых поясов, воспользуйтесь методом getAvailableIDs(). Обратите внимание, что это правило не применяется при вставке события с помощью намерения INSERT, как описано в разделе Использование намерения для вставки события, — в этом случае используется часовой пояс по умолчанию.
  • Для однократных событий необходимо указать DTEND.
  • Для повторяющихся событий необходимо указать DURATION в дополнение к RRULE или RDATE. Обратите внимание, что это правило не применяется при вставке события с помощью намерения INSERT, как описано в разделе Использование намерения для вставки события, — в этом случае можно использовать RRULE в сочетании с DTSTART и DTEND; кроме того, приложение «Календарь» автоматически преобразует указанный период в продолжительность.

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

long calID = 3;
long startMillis = 0;
long endMillis = 0;
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 9, 14, 7, 30);
startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 9, 14, 8, 45);
endMillis = endTime.getTimeInMillis();
...

ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Events.DTSTART, startMillis);
values.put(Events.DTEND, endMillis);
values.put(Events.TITLE, "Jazzercise");
values.put(Events.DESCRIPTION, "Group workout");
values.put(Events.CALENDAR_ID, calID);
values.put(Events.EVENT_TIMEZONE, "America/Los_Angeles");
Uri uri = cr.insert(Events.CONTENT_URI, values);

// get the event ID that is the last element in the Uri
long eventID = Long.parseLong(uri.getLastPathSegment());
//
// ... do something with event ID
//
//

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

Обновление событий

Когда ваше приложение хочет предоставить пользователю возможность изменить событие, мы рекомендуем использовать намерение EDIT, как описано в разделе Использование намерения для вставки события. Однако при необходимости вы можете редактировать события напрямую. Чтобы обновить событие, можно указать _ID события: либо в виде идентификатора, добавленного к URI(withAppendedId()), либо в качестве первого элемента выделения. Выделение должно начинаться с "_id=?", а первым аргументом selectionArg должен быть _ID события. Также можно обновлять выделения без идентификаторов. Ниже представлен пример обновления события. Это пример изменения названия события с помощью метода withAppendedId():

private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 188;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri updateUri = null;
// The new title for the event
values.put(Events.TITLE, "Kickboxing");
updateUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().update(updateUri, values, null, null);
Log.i(DEBUG_TAG, "Rows updated: " + rows);  

Удаление событий

Удалить событие можно по его _ID, который добавлен в качестве идентификатора к URI, или с помощью стандартного выделения. В случае использования добавленного идентификатора невозможно также выполнить и выделение. Существует две версии операции удаления: для приложения и для адаптера синхронизации. При удалении для приложения в столбце deleted устанавливается значение «1». Этот флаг сообщает адаптеру синхронизации о том, что строка была удалена и информацию об удалении следует передать серверу. При удалении для адаптера синхронизации событие удаляется из базы данных вместе со всеми связанными с ним данными. Ниже представлен пример удаления события для приложения по его _ID.

private static final String DEBUG_TAG = "MyActivity";
...
long eventID = 201;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
Uri deleteUri = null;
deleteUri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
int rows = getContentResolver().delete(deleteUri, null, null);
Log.i(DEBUG_TAG, "Rows deleted: " + rows);

Таблица участников

В каждой строке таблицы CalendarContract.Attendees указан один участник или гость события. При вызове метода query() возвращается список участников для события с заданным EVENT_ID. Этот EVENT_ID должен соответствовать _ID определенного события.

В таблице ниже указаны поля, доступные для записи. При вставке нового участника необходимо указать все эти поля, кроме ATTENDEE_NAME.

Константа Описание
EVENT_ID Идентификатор события.
ATTENDEE_NAME Имя участника.
ATTENDEE_EMAIL Адрес эл. почты участника.
ATTENDEE_RELATIONSHIP

Связь участника с событием. Одно из следующего:

ATTENDEE_TYPE

Тип участника. Одно из следующего:

ATTENDEE_STATUS

Статус посещения события участником. Одно из следующего:

Добавление участников

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

long eventID = 202;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Attendees.ATTENDEE_NAME, "Trevor");
values.put(Attendees.ATTENDEE_EMAIL, "trevor@example.com");
values.put(Attendees.ATTENDEE_RELATIONSHIP, Attendees.RELATIONSHIP_ATTENDEE);
values.put(Attendees.ATTENDEE_TYPE, Attendees.TYPE_OPTIONAL);
values.put(Attendees.ATTENDEE_STATUS, Attendees.ATTENDEE_STATUS_INVITED);
values.put(Attendees.EVENT_ID, eventID);
Uri uri = cr.insert(Attendees.CONTENT_URI, values);

Таблица напоминаний

В каждой строке таблицы CalendarContract.Reminders указано одно напоминание о событии. При вызове метода query()возвращается список напоминаний для события с заданным EVENT_ID.

В таблице ниже указаны поля, доступные для записи. При вставке нового напоминания необходимо указать все эти поля. Обратите внимание, что адаптеры синхронизации задают типы напоминаний, которые они поддерживают, в таблице CalendarContract.Calendars. Подробные сведения см. в справке по ALLOWED_REMINDERS.

Константа Описание
EVENT_ID Идентификатор события.
MINUTES Время срабатывания уведомления (в минутах) до начала события.
METHOD

Метод уведомления, заданный на сервере. Одно из следующего:

Добавление напоминаний

Ниже представлен пример добавления напоминания в событие. Напоминание срабатывает за 15 минут до начала события.

long eventID = 221;
...
ContentResolver cr = getContentResolver();
ContentValues values = new ContentValues();
values.put(Reminders.MINUTES, 15);
values.put(Reminders.EVENT_ID, eventID);
values.put(Reminders.METHOD, Reminders.METHOD_ALERT);
Uri uri = cr.insert(Reminders.CONTENT_URI, values);

Таблица экземпляров

В таблице CalendarContract.Instances содержатся данные о времени начала и окончания повторений события. В каждой строке этой таблицы представлено одно повторение события. Таблица экземпляров недоступна для записи; она предоставляет только возможность запрашивать повторения событий.

В таблице ниже перечислены некоторые из полей, которые можно запросить для экземпляра. Обратите внимание, что часовой пояс задается параметрами KEY_TIMEZONE_TYPE и KEY_TIMEZONE_INSTANCES.

Константа Описание
BEGIN Время начала экземпляра в формате UTC (в миллисекундах).
END Время окончания экземпляра в формате UTC (в миллисекундах).
END_DAY День окончания экземпляра по юлианскому календарю относительно часового пояса приложения «Календарь».
END_MINUTE Минута окончания экземпляра, вычисленная от полуночи по часовому поясу приложения «Календарь».
EVENT_ID _ID события для этого экземпляра.
START_DAY День начала экземпляра по юлианскому календарю относительно часового пояса приложения «Календарь».
START_MINUTE Минута начала экземпляра, вычисленная от полуночи по часовому поясу приложения «Календарь».

Запрос таблицы экземпляров

Чтобы запросить таблицу экземпляров, необходимо указать промежуток времени для запроса в URI. В этом примере CalendarContract.Instances получает доступ к полю TITLE посредством своей реализации интерфейса CalendarContract.EventsColumns. Другими словами, TITLE возвращается посредством обращения к базе данных, а не путем запроса таблицы CalendarContract.Instances с необработанными данными.

private static final String DEBUG_TAG = "MyActivity";
public static final String[] INSTANCE_PROJECTION = new String[] {
    Instances.EVENT_ID,      // 0
    Instances.BEGIN,         // 1
    Instances.TITLE          // 2
  };

// The indices for the projection array above.
private static final int PROJECTION_ID_INDEX = 0;
private static final int PROJECTION_BEGIN_INDEX = 1;
private static final int PROJECTION_TITLE_INDEX = 2;
...

// Specify the date range you want to search for recurring
// event instances
Calendar beginTime = Calendar.getInstance();
beginTime.set(2011, 9, 23, 8, 0);
long startMillis = beginTime.getTimeInMillis();
Calendar endTime = Calendar.getInstance();
endTime.set(2011, 10, 24, 8, 0);
long endMillis = endTime.getTimeInMillis();

Cursor cur = null;
ContentResolver cr = getContentResolver();

// The ID of the recurring event whose instances you are searching
// for in the Instances table
String selection = Instances.EVENT_ID + " = ?";
String[] selectionArgs = new String[] {"207"};

// Construct the query with the desired date range.
Uri.Builder builder = Instances.CONTENT_URI.buildUpon();
ContentUris.appendId(builder, startMillis);
ContentUris.appendId(builder, endMillis);

// Submit the query
cur =  cr.query(builder.build(),
    INSTANCE_PROJECTION,
    selection,
    selectionArgs,
    null);

while (cur.moveToNext()) {
    String title = null;
    long eventID = 0;
    long beginVal = 0;

    // Get the field values
    eventID = cur.getLong(PROJECTION_ID_INDEX);
    beginVal = cur.getLong(PROJECTION_BEGIN_INDEX);
    title = cur.getString(PROJECTION_TITLE_INDEX);

    // Do something with the values.
    Log.i(DEBUG_TAG, "Event:  " + title);
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(beginVal);
    DateFormat formatter = new SimpleDateFormat("MM/dd/yyyy");
    Log.i(DEBUG_TAG, "Date: " + formatter.format(calendar.getTime()));
    }
 }

Намерения календаря

Вашему приложению не нужно запрашивать разрешения на чтение и запись данных календаря. Вместо этого можно использовать намерения, поддерживаемые приложением «Календарь» Android, для обработки операций чтения и записи в этом приложении. В таблице ниже указаны намерения, поддерживаемые поставщиком календаря.

Действие URI Описание Дополнительные данные

VIEW

content://com.android.calendar/time/<ms_since_epoch>

Сослаться на URI также можно с помощью CalendarContract.CONTENT_URI. Пример использования этого намерения представлен в разделе Использование намерений для просмотра данных календаря.
Открытие календаря во время, заданное параметром <ms_since_epoch>. Отсутствуют.

VIEW

content://com.android.calendar/events/<event_id>

Сослаться на URI также можно с помощью Events.CONTENT_URI. Пример использования этого намерения представлен в разделе Использование намерений для просмотра данных календаря.
Просмотр события, указанного с помощью <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

content://com.android.calendar/events/<event_id>

Сослаться на URI также можно с помощью Events.CONTENT_URI. Пример использования этого намерения представлен в разделе Использование намерения для редактирования события.
Редактирование события, указанного с помощью <event_id>. CalendarContract.EXTRA_EVENT_BEGIN_TIME


CalendarContract.EXTRA_EVENT_END_TIME
EDIT

INSERT

content://com.android.calendar/events

Сослаться на URI также можно с помощью Events.CONTENT_URI. Пример использования этого намерения представлен в разделе Использование намерения для редактирования события.
Создание события. Любые из дополнительных данных, указанных в таблице ниже.

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

Дополнительные данные намерения Описание
Events.TITLE Название события.
CalendarContract.EXTRA_EVENT_BEGIN_TIME Время начала события (в миллисекундах) от эпохи.
CalendarContract.EXTRA_EVENT_END_TIME Время окончания события (в миллисекундах) от эпохи.
CalendarContract.EXTRA_EVENT_ALL_DAY Логическое значение, обозначающее, что это событие на весь день. Значение может быть true или false.
Events.EVENT_LOCATION Место проведения события.
Events.DESCRIPTION Описание события.
Intent.EXTRA_EMAIL Адреса эл. почты приглашенных (через запятую).
Events.RRULE Правило повторения для события.
Events.ACCESS_LEVEL Указывает на то, является ли событие общедоступным или закрытым.
Events.AVAILABILITY Если событие считается как занятое или как свободное время, доступное для планирования.

В разделах ниже указан порядок использования этих намерений.

Использование намерения для вставки события

С помощью намерения INSERT ваше приложение может отправлять задачи вставки события прямо в приложение «Календарь». Благодаря этому в файл манифеста вашего приложения не нужно включать разрешение WRITE_CALENDAR.

Когда пользователи работают с приложением, в котором используется такой подход, приложение отправляет их в «Календарь» для завершения добавления события. Намерение INSERT использует дополнительные поля для предварительного указания в форме сведений о событии в приложении «Календарь». После этого пользователи могут отменить событие, отредактировать форму или сохранить событие в своем календаре.

Ниже представлен фрагмент кода, в котором на 19 января 2012 г. планируется событие, которое будет проходить с 7:30 до 8:30. Однако следует учесть некоторые моменты, касающиеся этого примера кода.

  • В качестве URI в нем задается Events.CONTENT_URI.
  • В нем используются дополнительные поля CalendarContract.EXTRA_EVENT_BEGIN_TIME и CalendarContract.EXTRA_EVENT_END_TIME для предварительного указания в форме сведений о времени события. Значения времени должны быть указаны в формате UTC и в миллисекундах от эпохи.
  • В нем используется дополнительное поле Intent.EXTRA_EMAIL для предоставления списка участников, разделенных запятыми (их адреса эл. почты).
Calendar beginTime = Calendar.getInstance();
beginTime.set(2012, 0, 19, 7, 30);
Calendar endTime = Calendar.getInstance();
endTime.set(2012, 0, 19, 8, 30);
Intent intent = new Intent(Intent.ACTION_INSERT)
        .setData(Events.CONTENT_URI)
        .putExtra(CalendarContract.EXTRA_EVENT_BEGIN_TIME, beginTime.getTimeInMillis())
        .putExtra(CalendarContract.EXTRA_EVENT_END_TIME, endTime.getTimeInMillis())
        .putExtra(Events.TITLE, "Yoga")
        .putExtra(Events.DESCRIPTION, "Group class")
        .putExtra(Events.EVENT_LOCATION, "The gym")
        .putExtra(Events.AVAILABILITY, Events.AVAILABILITY_BUSY)
        .putExtra(Intent.EXTRA_EMAIL, "rowan@example.com,trevor@example.com");
startActivity(intent);

Использование намерения для редактирования события

Событие можно отредактировать напрямую, как описано в разделе Обновление событий. Благодаря намерению EDIT приложение, у которого нет разрешения, может делегировать редактирование события приложению «Календарь». По завершении редактирования события в приложении «Календарь» пользователи возвращаются в исходное приложение.

Ниже представлен пример намерения, который задает новый заголовок для указанного события и позволяет пользователям редактировать событие в приложении «Календарь».

long eventID = 208;
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_EDIT)
    .setData(uri)
    .putExtra(Events.TITLE, "My New Title");
startActivity(intent);

Использование намерений для просмотра данных календаря

Поставщик календаря позволяет использовать намерение VIEW двумя различными способами:

  • Открытие приложения «Календарь» на определенной дате.
  • Просмотр события.

Ниже представлен пример открытия приложения «Календарь» на определенной дате.

// A date-time specified in milliseconds since the epoch.
long startMillis;
...
Uri.Builder builder = CalendarContract.CONTENT_URI.buildUpon();
builder.appendPath("time");
ContentUris.appendId(builder, startMillis);
Intent intent = new Intent(Intent.ACTION_VIEW)
    .setData(builder.build());
startActivity(intent);

Ниже представлен пример открытия события для его просмотра.

long eventID = 208;
...
Uri uri = ContentUris.withAppendedId(Events.CONTENT_URI, eventID);
Intent intent = new Intent(Intent.ACTION_VIEW)
   .setData(uri);
startActivity(intent);

Адаптеры синхронизации

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

  • Адаптеру синхронизации необходимо указать, что он является таковым, задав для параметра CALLER_IS_SYNCADAPTER значение true.
  • Адаптеру синхронизации необходимо предоставить ACCOUNT_NAME и ACCOUNT_TYPE в качестве параметров запроса в URI.
  • Адаптер синхронизации имеет доступ на запись к большему числу столбцов, чем приложение или виджет. Например, приложение может изменять только некоторые характеристики календаря, такие как название, отображаемое имя, настройки видимости и синхронизации. Тогда как адаптер синхронизации имеет доступ не только к этим столбцам, но и ко многим другим его характеристикам, таким как цвет календаря, часовой пояс, уровень доступа, местоположение и т. д. Однако адаптер синхронизации ограничен задаваемыми им параметрами ACCOUNT_NAME и ACCOUNT_TYPE.

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

 static Uri asSyncAdapter(Uri uri, String account, String accountType) {
    return uri.buildUpon()
        .appendQueryParameter(android.provider.CalendarContract.CALLER_IS_SYNCADAPTER,"true")
        .appendQueryParameter(Calendars.ACCOUNT_NAME, account)
        .appendQueryParameter(Calendars.ACCOUNT_TYPE, accountType).build();
 }

Пример реализации адаптера синхронизации (который не связан с приложением «Календарь») представлен в статье, посвященной SampleSyncAdapter.