Calendar
A calendar is a system of organizing days for social purposes. It is composed of records, such as events and todos. The records consist of subrecords, such as alarms, attendees, or extensions. For example, if an event has a recurrence rule (meaning that it happens multiple times), separate instances are generated for each time the event occurs.
This feature is supported in mobile applications only.
Figure: Calendar model
The Calendar Service supports vCalendars.
The main features of the Calendar API include:
-
Calendar books
- Determine where the events and todos belong.
- Create calendar books using the local device (with no account), service providers, such as Google or Yahoo (with an account), or applications, such as ChatON, Joyn, or Facebook.
- Search and organize events using filters and queries.
- Monitor database changes.
Each account can have multiple calendar books. The calendar book name does not need to be unique on the device because it is handled with an ID. Since the local device address book has no account, its related account ID is zero.
-
Events and tasks
- Set properties, such as summary, start time, and description.
- Set reminders.
-
Time management
- Define the calendar UTC time and date.
The following figure illustrates the different Calendar entities and their relationships.
Figure: Calendar entities
vCalendar supports ver1.0 (vcs) and 2.0 (ics). For more information about vCalendar, see rfc2445.
The calendar service module works in a way similar to client-service architecture. In this architecture, the Tizen application is the client side and has to connect to service before using calendar service APIs. Use calendar_connect() and calendar_disconnect() for connecting and disconnecting.
calendar_connect(); // Jobs for records calendar_disconnect();
Calendar Books
A calendar book is a placeholder for other records in the Calendar API. Every event and todo has to belong to a calendar book. There are 3 built-in calendar books, as shown in the following table.
Book | Description |
---|---|
DEFAULT_EVENT_CALENDAR_BOOK_ID | Event book |
DEFAULT_TODO_CALENDAR_BOOK_ID | Todo book |
DEFAULT_BIRTHDAY_CALENDAR_BOOK_ID | Birthday book |
calendar_record_h event = NULL; calendar_record_create(_calendar_event._uri, &event); // Set default calendar book id calendar_record_set_int(event, _calendar_event.calendar_id, DEFAULT_EVENT_CALENDAR_BOOK_ID); // Set other fields int event_id = 0; calendar_db_insert_record(event &event_id); // Destroy calendar_record_destroy(event, true);
To receive a list of existing calendar books, use the following code:
calendar_list_h calendar_book_list = NULL; calendar_db_get_all_records(_calendar_calendar_book._uri, 0, 0, &calendar_book_list);
In the calendar_db_get_all_records() function, you need as parameters the URI of the view to get records from, the index from which results are received, the maximum number of results, and the record list.
Database Change Notifications
To detect the calendar database changes, register a callback with the calendar_db_add_changed_cb() function. To unregister the callback and ignore database changes, use the calendar_db_remove_changed_cb() function.
Clients wait calendar change notifications on the client side. If the calendar is changed by another module, the server publishes an inotify event. The Inotify module broadcasts to the subscribed modules, and an internal inotify handler is called at the client side. A user callback function is called with the user data.
// Add callback function void __event_changed_ cb(const char *view_uri, void *user_data) {} // Add change notification callback calendar_db_add_changed_cb(_calendar_event._uri, __event_changed_cb, NULL);
Events and Instances
An event record describes various properties, such as description, categories, and priority. It also contains information on when the event takes place. In a recurring event, there are more than one instance of the event. Each instance has its corresponding instance record.
If an event is inserted with rrule (recurrence rule), alarm, and attendee, its data is saved to each database. Generated instances based on the rrule are also stored in the instance database.
Figure: Views and databases
The following table illustrates and example of a recurring event and its instances.
Event | Instances |
---|---|
Recurrence rules:
|
2012-10-09 Tuesday
2012-10-16 Tuesday 2012-10-22 Tuesday |
The recurrence model in the Calendar API is compliant with the iCalendar specification. The following event properties have the same functionality as their corresponding values in iCalendar:
Recurrence rule property | Description |
---|---|
freq | Yearly, monthly, weekly, or daily |
count | Until count. If the count is 3, 3 instances are generated. |
interval | Interval is a positive integer representing how often the recurrence rule repeats |
byday | MO, TU, WE, TH, FR, SA, or SU |
bymonthday | Days of the month |
byyearday | Days of the year |
byweekno | Ordinals specifying weeks of the year |
bymonth | Months of the year |
bysetpos | Values which correspond to the nth occurrence within the set of events |
wkst | Day on which the workweek starts |
Exceptions
If one of the instances of a recurring event is modified (such as its summary or date) or deleted, it is called an exception. For example, if 2nd instance date is modified from 16th to 17th, 17th is the exception.
Event | Instances | Exceptions |
---|---|---|
Recurrence rules:
|
2012-10-09 Tuesday
2012-10-16 Tuesday > modified 2012-10-22 Tuesday |
2012-10-17 Tuesday |
To get the changes in an exception, use the calendar_db_get_changes_exception_by_version() function. The instances and exceptions are deleted together when the original event is deleted.
Filters and Queries
Queries are used to retrieve data which satisfies a given criteria, like an integer property being greater than a given value, or a string property containing a given substring. The criteria are defined by creating filters and adding conditions to them, joining them with logical operators. Also, instead of a condition, another filter can be added to create more complex filters.
When a filter is ready, it can be set as a property of a query. Other query properties allow configuring how the returned results are grouped and sorted.
The operator precedence in filters is determined by the order in which the conditions and filters are added. The following table shows an example of how the operator precedence works.
Condition | Result |
---|---|
Condition C1 OR Condition C2 AND Condition C3 |
(C1 OR C2) AND C3 |
Filter F1: Condition C1 OR Condition C2 Filter F2: Condition C3 OR Condition C4 Filter F3: Condition C5 AND F1 AND F2 |
(C5 AND F1) AND F2 Meaning (C5 AND (C1 OR C2)) AND (C3 OR C4) |
The following code creates a filter, accepting events with high priority or those that include the word "meeting" in their description.
calendar_filter_h filter = NULL; // Create a filter returning event type records calendar_filter_create(_calendar_event._uri, &filter); // Add a 'priority equals high' condition calendar_filter_add_int(filter, _calendar_event.priority, CALENDAR_MATCH_EQUAL, CALENDAR_EVENT_PRIORITY_HIGH); // Add OR operator calendar_filter_add_operator(filter, CALENDAR_FILTER_OPERATOR_OR); // Add a 'description contains "meeting"' condition calendar_filter_add_str(filter, _calendar_event.description, CALENDAR_MATCH_CONTAINS, "meeting");
Insert the filter into the query and execute the query:
calendar_query_h query = NULL; calendar_list_h list = NULL; // Create a query returning event type records calendar_query_create(_calendar_event._uri, &query); // Add the filter calendar_query_set_filter(query, filter); // Execute the query, results are returned in a list calendar_db_get_records_with_query(query, 0, 0, &list); calendar_filter_destroy(filter); calendar_query_destroy(query); // Use the list calendar_list_destroy(list, true);
Projections
Projection is related to searching with filters and queries. A projection allows you to query the data for just those specific properties of a record that you actually need, at lower latency and cost than retrieving the entire set of properties.
The following example code creates a filter that gets only the event ID, summary, and start time from those records whose summary has "test" (string filter). It creates a query, adds the filter to it, and receives the results in a list.
calendar_query_h query = NULL; calendar_filter_h filter = NULL; // Set query with filter calendar_query_create(_calendar_event_calendar_book_attendee._uri, &query); calendar_filter_create(_calendar_event_calendar_book_attendee._uri, &filter); calendar_filter_add_str(filter, _calendar_event.summary, CALENDAR_MATCH_CONTAINS, "test"); calendar_query_set_filter(query, filter); // Set projection unsigned int projection[3]; projection[0] = _calendar_event_calendar_book_attendee.event_id; projection[1] = _calendar_event_calendar_book_attendee.summary; projection[2] = _calendar_event_calendar_book_attendee.start_time; // Get list calendar_query_set_projection(query, projection, 3); calendar_db_get_records_with_query(query, 0, 0, &list); // Destroy handle calendar_filter_destroy(filter); calendar_query_destroy(query); calendar_list_destroy(list, true);
Records
An important concept in the Calendar Service APIs is a record. Although a record represents an actual record in the internal database, you can consider it as a structure describing a single (but complex) entity, like a calendar event or a time zone.
A record has many properties, for example, a todo record has the todo description, priority, progress, creation time, last modified and completed time, and many others.
A record can also contain an identifier field, which holds an ID of another record. Setting this field's value establishes a relation between the records, for example, a calendar event contains the ID of a calendar book to which it belongs.
URI
A record type is identified by a structure called the view. For example, the _calendar_event view describes the properties of the calendar event record. Every view has a special field - _uri - that uniquely identifies the view. In many cases, you must provide the _uri value to indicate what type of record you want to create or operate on.
// Create an event with _calendar_event view calendar_record_h record = NULL; calendar_record_create(_calendar_event._uri, &record);
Record Handle
To use a record, you must obtain its handle. You can use many methods to obtain the handle, for example, you can create a new record or refer to the child records of a record.
// Create an event and get a handle calendar_record_h event = NULL; calendar_record_create(_calendar_event._uri, &event); // Get the record handle with ID calendar_record_h event2 = NULL; calendar_db_get_record(_calendar_event._uri, event_id, &event2);
Basic Types
Records contain properties of basic types: integer, lli (long integer, long long int), double, string, bool, and time. The time type holds either a long long int, or 3 integers (year, month, day).
The following table lists the setter and getter functions for each type.
Property | Setter | Getter |
---|---|---|
integer | calendar_record_set_int | calendar_record_get_int |
long long integer | calendar_record_set_lli | calendar_record_get_lli |
double | calendar_record_set_double | calendar_record_get_double |
string | calendar_record_set_str | calendar_record_get_str |
calendar_time_s | calendar_record_set_caltime | calendar_record_get_caltime |
Children
Certain type of records hold "child list" properties. If a record has a property of this type, it can be a parent of other records, called child records. For example, the attendee records can hold an event identifier in their event_id property. The event is the parent record of the child attendee records.
The following code example creates an event and inserts it into the default event book.
// Create an event calendar_record_h event = NULL; calendar_record_create(_calendar_event._uri, &event); // Set event summary calendar_record_set_str(event, _calendar_event.summary, "Meeting"); // Put the event into the default calendar book for events calendar_record_set_int(event, _calendar_event.calendar_book_id, book_id); // Insert calendar book into the database int event_id = 0; calendar_db_insert_record(event, &event_id); // Destroy calendar_record_destroy(event, true);
Reminders
The following figure illustrates how the alarm process works.
Figure: Alarm process
To get a reminder when an alarm is triggered, the application must set the reminder mime name. After the reminder mime name is set, insert an alarm as a child of an event record:
// Set alarm calendar_record_h alarm = NULL; calendar_record_create(_calendar_alarm._uri, &alarm); calendar_record_set_int(alarm, _calendar_alarm.tick_unit, CALENDAR_ALARM_TIME_UNIT_SPECIFIC); calendar_record_set_lli(alarm, _calendar_alarm.time, (1404036000 - 60)); // Before 60 sec // Add alarm as child calendar_record_add_child_record(event, _calendar_event.calendar_alarm, alarm);
When the registered alarm is triggered and the alarm manager notices it, the calendar service calls those packages that have the reminder mime name.
Time Structure
The calendar time structure, calendar_caltime_s, is defined as follows:
typedef struct { calendar_time_type_e type; union { long long int utime; struct { int year; int month; int mday; } date; } time; } calendar_time_s;
Use this structure when setting the calendar time (_CALENDAR_PROPERTY_CALTIME) properties of the records.
The time structure can hold 2 types of data: UTC time (long long int) and date, given as year, month, and day of the month (3 integers). These types are identified by the values of the calendar_time_type_e variable, and they determine the usage of the structure.
Identifier | Type | Name | Purpose |
---|---|---|---|
CALENDAR_TIME_UTIME | long long int | utime | UTC time, used to describe non-all-day events |
CALENDAR_TIME_LOCALTIME | struct | date | Date only (year, month, and day of the month), used to describe all day events. |
UTC Time Usage
Structures with UTC time are used for non-all-day events. In such cases, you must convert local time to UTC time. The local time zone identifier must be stored in the record, in the corresponding property.
For example, when setting the start time of an event, the local time zone must be stored in the start_tzid property.
When converting local time to UTC time, use the function illustrated in the following example. It converts the given date and time to the corresponding UTC time, considering the given time zone (first argument). The function uses the i18n API.
#define ms2sec(ms) (long long int)(ms / 1000.0) long long int _time_convert_itol(char *tzid, int y, int mon, int d, int h, int min, int s) { int ret = 0; i18n_uchar utf16_timezone[64] = {0}; i18n_ustring_copy_ua_n(utf16_timezone, tzid, sizeof(utf16_timezone)/sizeof(i18n_uchar)); i18n_ucalendar_h ucal = NULL; char *loc_default = NULL; i18n_ulocale_get_default((const char **)&loc_default); ret = i18n_ucalendar_create(utf16_timezone, -1, loc_default, I18N_UCALENDAR_GREGORIAN, &ucal); if (I18N_ERROR_NONE != ret) { dlog_print(DLOG_DEBUG, LOG_TAG, "i18n_ucalendar_create() is failed (%d)\n", ret); return -1; } i18n_ucalendar_set_date_time(ucal, y, mon - 1, d, h, min, s); i18n_udate date; ret = i18n_ucalendar_get_milliseconds(ucal, &date); if (I18N_ERROR_NONE != ret) { dlog_print(DLOG_DEBUG, LOG_TAG, "i18n_ucalendar_create() is failed (%d)\n", ret); i18n_ucalendar_destroy(ucal); return -1; } i18n_ucalendar_destroy(ucal); return ms2sec(date); }
// Fill calendar time structures (start and end time) calendar_time_s st = {0}; calendar_time_s et = {0}; st.type = CALENDAR_TIME_UTIME; st.time.utime = _time_convert_itol("Asia/Seoul", 2012, 9, 15, 11, 0, 0); et.type = CALENDAR_TIME_UTIME; et.time.utime = _time_convert_itol("Asia/Seoul", 2012, 9, 15, 12, 0, 0); // Create an event record // Set local time zone of start time calendar_record_set_str(event, _calendar_event.start_tzid, "Asia/Seoul"); // Set start time calendar_record_set_caltime(event, _calendar_event.start_time, st); // Set local time zone of end time calendar_record_set_str(event, _calendar_event.end_tzid, "Asia/Seoul"); // Set end time calendar_record_set_caltime(event, _calendar_event.end_time, et);
Date Usage
Another usage of the time structure is an all day event. In case of such events, the structure type field must be set to CALENDAR_TIME_LOCALTIME. Only the date (no time) is stored. Such structures can be used to set the start and end time of an event.
Both start and end time of the event must be set, and they do not have to be equal. If they are not, the event lasts more than 1 day. Note that in such cases there are no instances created, as this is still a non-recurring event.
Views and Properties
Generic access functions can be used (according to data-view declarations) to access calendar views. A data-view is almost the same as a database "VIEW", which limits access and guarantees performance. A "record" represents a single row of the data-views.
The _calendar_instance_utime and _calendar_instance_localtime views are not offered, but combinations with another views are provided.
Editable view | Read-only view |
---|---|
_calendar_book _calendar_event _calendar_todo _calendar_timezone _calendar_attendee _calendar_alarm _calendar_extended_property |
_calendar_updated_info _calendar_event_calendar_book _calendar_todo_calendar_book _calendar_event_calendar_book_attendee _calendar_instance_utime_calendar_book _calendar_instance_localtime_calendar_book _calendar_instance_utime_calendar_book_extended _calendar_instance_localtime_calendar_book_extended |
The _calendar_updated_info view is used when identifying record changes depending on the version. The other read-only views are a combination of editable views for UI convenience:
- _calendar_event + _calendar_book = _calendar_event_calendar_book
- _calendar_instance_utime + _calendar_book = _calendar_instance_utime_calendar_book
- _calendar_event + _calendar_book + _calendar_attendee = _calendar_event_calendar_book_attendee
Properties
The record types that have *_id as their property hold identifiers of other records. For example, the attendee and alarm views hold the ID of their corresponding events or todos in the event_id or todo_id property (as children of the corresponding event or todo records).
The record type properties are other records. For example, an event record has attendee and alarm properties, which means that records of those types can be children of the event type records. The following figure illustrates macros in a calendar_view.h header file.
Figure: Properties
// Create an event with _calendar_event view calendar_record_h event = NULL; calendar_record_create(_calendar_event._uri, &event); // Set event summary to _calendar_event view calendar_record_set_str(event, _calendar_event.summary, "Meeting");
Version
The calendar service uses a version system in the following APIs:
int calendar_db_get_current_version(int *calendar_db_version); int calendar_db_get_changes_by_version(..., int *current_calendar_db_version); int calendar_db_get_last_change_version(int *last_change_version); int calendar_db_get_changes_exception_by_version(..., int calendar_db_version, ...);
Whenever modifications are made in the database, the version number is increased. If sync applications, such as Google or Facebook, sync at version 13 and try to sync again every 1 minute, they want to get the changes from version 14 to the current version.
To get the current version, the calendar_db_get_current_version() function is used. The calendar_db_get_changes_by_version() function retrieves the modified record list. The calendar_db_get_changes_exception_by_version() function is used to get modified instances in a recurring event.
vCalendar
Use vCalendar to exchange personal calendar and schedule information. In order to avoid confusion with this referenced work, this is to be known as the vCalendar specification. vCalendar ver2.0 is known as iCalendar. The following snippet shows an example of the vCalendar.
BEGIN:VCALENDAR VERSION:2.0 PRODID:-//hacksw/handcal//NONSGML v1.0//EN BEGIN:VEVENT DTSTART:19970714T170000Z DTEND:19970715T035959Z SUMMARY:Bastille Day Party END:VEVENT END:VCALENDAR
The calendar service provides APIs to compose a vCalendar stream. With the stream, it is possible to create files transmit data with JSON data.
calendar_list_h list = NULL; // Create or get list to make vcalendar stream char *stream = NULL; calendar_vcalendar_make_from_records(list, &stream); // Jobs for stream // Free free(stream);
vCalendar can be parsed with calendar service APIs as well.
// Read stream from file calendar_list_h list = NULL; calendar_vcalendar_parse_to_calendar(stream, &list); // Jobs for list calendar_list_destroy(list, true);