Mobile native

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

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

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.

Table: Calendar books
Book Description
DEFAULT_EVENT_CALENDAR_BOOK_ID Event book
DEFAULT_TODO_CALENDAR_BOOK_ID Todo book
DEFAULT_BIRTHDAY_CALENDAR_BOOK_ID Birthday book
The following code example sets a calendar book ID.
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

Views and databases

The following table illustrates and example of a recurring event and its instances.

Table: Event and instance example
Event Instances
Recurrence rules:
  • Start date on 2012-10-09 (Tuesday)
  • Frequency set to WEEKLY
  • Interval set to 1
  • Count set to 3
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:

Table: Recurrence rules
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.

Table: Exception example
Event Instances Exceptions
Recurrence rules:
  • Start date on 2012-10-09 (Tuesday)
  • Frequency set to WEEKLY
  • Interval set to 1
  • Count set to 3
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.

Table: Filter conditions
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.

Table: Setter and getter functions
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

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.

Table: Data types
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.

Table: Calendar views
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

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);
Go to top