Tizen Native API
|
The Calendar Service API provides functions for managing calendars(including events, to-dos). This API allows you not only to store information about calendar but also to query calendar information.
Required Header
#include <calendar.h>
Overview
A calendar is a system of organizing days for social purposes and is composed of records like events and todos. These records are made up of another sub records like alarms, attendees or extended ones. Events could have recurrence rule, instances would be generated.
The Calendar-Service provides an interface to manage the information of records.
* Managing information stored in database. * Aggregating information from various accounts. * Notifying changes of information. * Searching information. * Vcalendar supports ver1.0(vcs) / 2.0(ics). |
Calendar service module works in a way similar to client-service architecture. In this architecture Tizen application is a client side and has to connect to service before using calendar service APIs. Connection/disconnection MUST be done with use of calendar_connect() / calendar_disconnect().
calendar_connect(); // jobs for records calendar_disconnect(); *
Entities
Calendar service manages information related to following entities.
- Calendar books
- Represents where event and todo should belong to.
- Created by one of calendars sources below
- Local device, which has no account.
- Service providers such as Google or Yahoo, with account.
- Applications like ChatON, Joyn, Facebook, etc.
- Have properties like name, account id, color.
- Events
- Have properties like summary, start_time, description.
- Single entry can be available on each calendar book.
- Todos
- Similar with event entry.
Relationship between entities
Multiple Address books from Accounts
Each account can create multiple calendar books. Calendar book name does not need to be unique on the device because it is handled with id. Local device address book has no account and its related account id is zero.
Views
View/Property To access and handle entities, views are provided.
According to data-view declarations, generic access functions are used (calendar_db_insert_record(), calendar_record_get_int(), …).
Data-View is almost same as database "VIEW" which limits access and guarantees the performance by offering various views for the proper purpose.
_calendar_instance_utime, _calendar_instance_localtime views are not offered but combination with another view 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_updated_info is used when identifying record changes depending on version.
The other read only views are 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
Record types which have *_id as their properties, hold identifiers of other records - e.g. attendee and alarm views hold id of their corresponding events or to-dos in event_id or todo_id property respectively (as children of the corresponding events or to-dos record). Properties of type 'record' are other records. For example, a event record has 'attendee' and 'alarm', which means that records of those types can be children of event type records.
In calendar_view.h header file, view macros are found and below figure. show what macro means.
There is an example how to create event with view.
// 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
Calendar service uses version system in bellow APIs.
Whenever modifications are in DB, version number is increased every time.
If sync applications like google, facebook sync at version 13 and they try to sync again every 1 minute, they want to get changes from version 14 to current version.
To get current version, calendar_db_get_current_version() is used and calendar_db_get_changes_by_version() is used to get the modified record list. calendar_db_get_changes_exception_by_version() is used to get modified instances in recurring event. (see exception in Events and instances)
Records
An important concept in Calendar service APIs is a record. It may be helpful to know that a record represents an actual record in the internal database, but in general, you can think of a record as a structure describing a single (but complex) view, like a calendar event or a time zone.
A record has many properties, for example, a to-do record has the to-do's description, priority, progress, created, last modified and completed time, plus many others.
A record can also contain an identifier field, which holds an identifier of another record. Setting this field's value establishes a relation between the records, for example, a calendar event contains the identifier of a calendar book to which it belongs.
URI
A record's 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 will need to provide the _uri value to indicate what type of record you wish to create or operate on.
// create an event and get handle calendar_record_h event = NULL; calendar_record_create(_calendar_event._uri, &event);
Record handle
To use a record, you must obtain its handle. There are many ways to obtains it, including creating a new record and referring to child records of a record.
// create an event and get handle calendar_record_h event = NULL; calendar_record_create(_calendar_event._uri, &event); // get 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 and calendar_time_s.
The calendar_time_s type holds either a long long int, or three integers (year, month, day). There are 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 |
A record's 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 will need to provide the _uri value to indicate what type of record you wish to create or operate on.
Child
Records of certain type also hold 'child list' properties. If a record has property of this type, it can be a parent of other records, called child records. For example, attendee records can hold an event's identifier in their event_id property. The event is the parent record of the child attendee records.
To use a record, you must obtain its handle. There are many ways to obtains it, including creating a new record and referring to child records of a record.
Sample code:
the code below creates an event and inserts it into default event book (see below on calendar books).
// create an event calendar_record_h event; 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); // add alarm as child 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_MINUTE); calendar_record_set_int(alarm, _calendar_alarm.tick, 5); calendar_record_add_child_record(event, _calendar_event.calendar_alarm, alarm); // insert calendar book into the database int event_id = 0; calendar_db_insert_record(event, &event_id); // destroy calendar_record_destroy(event, true);
Calendar Books
A calendar book is a placeholder for other records in Calendar API. Every event and to-do has to belong to a calendar book. There are three built-in calendar books.
DEFAULT_EVENT_CALENDAR_BOOK_ID | Event book |
DEFAULT_TODO_CALENDAR_BOOK_ID | Todo book |
DEFAULT_BIRTHDAY_CALENDAR_BOOK_ID | Birthday book |
There is an example how calendar book id is set.
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:
calendar_list_h calendar_book_list = NULL; calendar_db_get_all_records(_calendar_calendar_book._uri, 0, 0, &calendar_book_list);
Events and Instances
Two important concepts are event and instance. An event record describes various properties of the event, like description, categories, priority and many others. It also contains information on when the event takes place, there can be more than one instance of the event. Each instance has its corresponding instance record.
If event is inserted with rrule, alarm and attendee, its data is saved to each database. Generated instances based on rrule are also stored in instance database.
For example, if an event has the following properties:
event | instances |
---|---|
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-23 Tuesday |
Interval is a multiplier of frequency, which means that if it is set to N, instances occur every N weeks (or whatever was set in frequency attribute).
The recurrence model in Calendar API is compliant with iCalendar specification (www.ietf.org/rfc/rfc2445.txt). The following event properties have the same functionality as their corresponding values in iCalendar:
Recurrence rule property | comment |
---|---|
freq | Yearly, monthly, weekly, daily |
count | Until count. If count is 3, 3 instances are generated |
interval | The interval is positive integer representing how often the recurrence rule repeats |
byday | MO, TU, WE, TH, FR, SA, SU |
bymonthday | Days of month |
byyearday | Days of year |
byweekno | Ordinals specifying weeks of the year |
bymonth | Months of year |
bysetpos | Values which corresponds to the nth occurrence within the set of events |
wkst | The day on which the workweek starts |
Exceptions
If one of instances is modified in summary, date… or deleted, this is called exception.
For example, if 2nd instance date is modified from 16th to 17th, 17th is the exception.
event | instances | exceptions |
---|---|---|
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-23 Tuesday | 2012-10-17 Tuesday |
To get changes in exception, calendar_db_get_changes_exception_by_version() is called. These instances and exceptions are deleted together when original event is deleted.
Calendar 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;
The structure should be used when setting the calendar time type (_CALENDAR_PROPERTY_CALTIME) properties of records.
It can hold two types of data: UTC time (long long int) and date, given as year, month and day of the month (three integers). These types are identified by values of calendar_time_type_e, which are CALENDAR_TIME_UTIME and CALENDAR_TIME_LOCALTIME, respectively. The data type determines 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 should be used for non-all-day events. In such cases, the API user should convert local time to UTC time. The local time zone identifier should be stored in the record, in the corresponding property.
For example, when setting starting time of an event, the local time zone should be stored in start_tzid.
When converting local time to UTC time, the function below can be useful. The function converts the given date and time to the corresponding UTC time, considering the given time zone (first argument).
#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[CAL_STR_SHORT_LEN64] = {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() Fail (%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_millisecond(ucal, &date); if (I18N_ERROR_NONE != ret) { dlog_print(DLOG_DEBUG, LOG_TAG, "i18n_ucalendar_create() Fail (%d)\n", ret); i18n_ucalendar_destroy(ucal); return -1; } i18n_ucalendar_destroy(ucal); return ms2sec(date); }
Sample code:
// 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 time structure is an all day event. In case of such events, the structure's type field MUST be set to CALENDAR_TIME_LOCALTIME. Only the date (no time) will be stored. Such structures can be used to set start and end time of an event.
Both start and end time of the event MUST be set. The end date value MUST be later in time than the value of the start date.
// range is 2days: 2014/02/17 00:00:00 ~ 2014/02/19 00:00:00 calendar_time_s st = {0}; st.type = CALENDAR_TIME_LOCALTIME; st.time.date.year = 2014; st.time.date.month = 2; st.time.date.mday = 17; calendar_time_s et = {0}; et.type = CALENDAR_TIME_LOCALTIME; et.time.date.year = 2014; et.time.date.month = 2; et.time.date.mday = 19; // create an event record // ...
Recurring Events
To create a recurring event in Calendar API, you MUST set frequency. There is a sample code how to create recurring event. Firstly, set start and end time.
calendar_time_s st = {0}; st.type = CALENDAR_TIME_UTIME; st.time.utime = _time_convert_itol("Asia/Seoul", 2012, 9, 15, 11, 0, 0); calendar_time_s et = {0}; et.type = CALENDAR_TIME_UTIME; et.time.utime = _time_convert_itol("Asia/Seoul", 2012, 9, 15, 12, 0, 0);
Then, the remaining properties should be set. Each frequency needs other proper fields except daily event. If other values are not inserted, these values are calculated based on start time.
Freq | Property | Comment |
---|---|---|
CALENDAR_RECURRENCE_YEARLY | byyearday | Every 100th day |
byweekno | Every 34th week | |
bymonthday | Every 1st February (birthday) | |
byday | Every 1st Monday of May (holiday) | |
CALENDAR_RECURRENCE_MONTHLY | bymonthday | Every 29th (payday) |
byday | Every last Friday | |
CALENDAR_RECURRENCE_WEEKLY | byday | Every week |
CALENDAR_RECURRENCE_DAILY | - | - |
If byday is not set in weekly event, default byday is followed start day of week. By the same token, default interval is 1 and default range type is endless.
Range type | Comment |
---|---|
CALENDAR_RANGE_NONE | Endless(max date is 2036/12/31) |
CALENDAR_RANGE_UNTIL | Should set until property |
CALENDAR_RANGE_COUNT | Should set count property |
calendar_record_h event; calendar_record_create(_calendar_event._uri, &event); calendar_record_set_str(event, _calendar_event.start_tzid, "Asia/Seoul"); calendar_record_set_caltime(event, _calendar_event.start_time, st); calendar_record_set_str(event, _calendar_event.end_tzid, "Asia/Seoul"); calendar_record_set_caltime(event, _calendar_event.end_time, et); calendar_record_set_int(event, _calendar_event.freq, CALENDAR_RECURRENCE_WEEKLY); calendar_record_set_int(event, _calendar_event.interval, 1) calendar_record_set_int(event, _calendar_event.count, 3);
The last step is inserting the event into the database. Records representing instances of the event are created when the event record is inserted.
int event_id = 0; calendar_db_insert_record(event, &event_id); calendar_record_destroy(event, true);
Filters and Queries
Queries are used to retrieve data which satisfies 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, which can be used to create more complex filters.
Operator precedence in filters determined by the order in which the conditions and filters are added.
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.
Operator precedence in filters is determined by the order in which the conditions and filters are added are added. For example, if the following sequence is added:
Filter with conditions | Result |
---|---|
Contidion C1 OR Contidion 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 Which is: (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 '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 'description contains "meeting"' condition calendar_filter_add_str(filter, _calendar_event.description, CALENDAR_MATCH_CONTAINS, "meeting");
The filter should be inserted into a query and the query should be executed:
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
Useful concept in Calendar service APIs is projection, related to searching with filters and queries. 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 properties
Sample code:
create filter which will get event id, summary and start time from the record with its summary has “test” (string filter). Create a query and add the filter to it. Results are received 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);
Reminders
Alarm and reminder is the similar terminology but reminder is used as package name storages in below figure 5. When alarm alerts, calendar-service sends notification to all packages which is registered in reminder DB table with calendar_reminder_add_receiver().
This shows how to set alarm and how alarm works.
After adding receiver, registered alarm would be alerted on reserved time by inserting alarm as child.
tick_unit | related field | comment |
---|---|---|
CALENDAR_ALARM_TIME_UNIT_SPECIFIC | time | This represents the number of seconds elapsed since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) |
CALENDAR_ALARM_TIME_UNIT_WEEK | tick | The number of weeks before start time |
CALENDAR_ALARM_TIME_UNIT_DAY | tick | The number of days before start time |
CALENDAR_ALARM_TIME_UNIT_HOUR | tick | The number of hours before start time |
CALENDAR_ALARM_TIME_UNIT_MINUTE | tick | The number of minutes before start time |
Below example shows the alarm which is set 1 minute before start time.
// set alarm with normal unit 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_MINUTE); calendar_record_set_int(alarm, _calendar_alarm.tick, 1); // before 1min (60secs) // add alarm as child calendar_record_add_child_record(event, _calendar_event.calendar_alarm, alarm);
With CALENDAR_ALARM_TIME_UNIT_SPECIFIC, the alarm could be set regardless of the start time.
// set alarm with specific unit 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); // suppose start time is 1404036000(Sun, 29 Jun 2014 10:00:00 GMT) and alarm is set 60secs after from start time. calendar_time_s ct; ct.type = CALENDAR_TIME_UTIME; ct.time.utime = 1404036000 + 60; calendar_record_set_caltime(alarm, _calendar_alarm.alarm_time, ct); // add alarm as child calendar_record_add_child_record(event, _calendar_event.calendar_alarm, alarm);
How to register package as reminder.
In manifest.xml file, below code must be inserted.
<app-control> <operation name="http://tizen.org/appcontrol/operation/view" /> <mime name="application/x-tizen.calendar.reminder" /> </app-control>
When alarm alerts, calendar-service sends data with key, value pairs.
With "ids" key, id array could be get.
Each detail value could be get with "each id" key.
The detail data is the combination of alarm values. id, time, tick, unit and type is connected with "&" charater.
ex> id=64&time=1415106300&tick=0&unit=60&type=0
// "ids" string is the key, to get id array char **ids = NULL; int len = 0; app_control_get_extra_data_array(b, "ids", &ids, &len); int i = 0; for (i = 0; i < len; i++) { // "id" is the key to get detail value char *value = NULL; app_control_get_extra_data(b, ids[i], &value); if (NULL == value) { continue; } // parse detail data // free free(ids[i]); ids[i] = NULL; } free(ids);
Database Change Notifications
Applications add/remove callback function to detect/ignore the calendar DB changes with calendar_db_add_changed_cb() / calendar_db_remove_changed_cb().
Clients wait calendar change notification on client side. If calendar is changed by another module, server publishes inotify event. Inotify module broadcasts to subscribe modules. Internal inotify handler is called at client side. User callback function is called with user data.
// add callback function void __event_changed_cb(const char *view_uri, void *user_data) { } // add changed noti callback calendar_db_add_changed_cb(_calendar_event._uri, __event_changed_cb, NULL);
Vcalendar
To exchange of personal calendaring and scheduling information, vcalendar is used. 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.
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 |
Calendar service provides APIs to compose vcalendar stream. With stream, file could be made or data could be transmitted 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 could 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);