The purpose of this document is to introduce users of the Agenda Model delivered in Symbian OS versions 8.1 and earlier to the new API which is delivered in Symbian OS v9.1. The document is aimed at Agenda application developers who will be required to migrate their old applications to make use of the new Calendar API (CalInterimAPI).
The release of Symbian OS v9.1 brings many new features and also includes a change in compiler. This compiler change results in a binary incompatibility with previous versions and will require all users to rebuild their products.
The Agenda Model has been present in all releases of Symbian OS and over the years the needs of mobile calendar users have evolved. The break in compatibility for version 9.1 is the perfect opportunity to make preparations for changes in upcoming releases.
Detailed analysis shows that very few applications use a large proportion of the agenda model's API set. There are also features which are no longer required in mobile devices. In order to simplify the implementation behind the API and to allow for future improvements, the Agenda Model API has been replaced with the CalInterimAPI.
Collaborative calendaring (also known as Group Scheduling) today relies on the use of a number of RFC standards commonly referred to as iCalendar. In addition to a rationalisation of the API, some new functionality has been added to enable agenda applications to implement group scheduling.
The API presented by the CalInterimAPI has the following features:
It rationalises the Agenda Model by removing a number of redundant classes and functions. The number of APIs has been reduced to less than 200.
New fixed time behaviour allows agenda entries and alarms to be displayed with correctly adjusted date and time when the user has moved into a different time zone, e.g. there is no need to re-queue alarms when this happens.
Instead of TTime
,
TAgnDate
and TAgnDateTime
, a new
class TCalTime
is introduced which is used for all APIs.
This requires the client to set the time to system local or UTC time explicitly
before passing the time parameters to the functions.
Partially supported iCalender (iCal) features. It allows the user to create and store entries with data that conforms to the iCalendar standard.
Meeting participation enhancement - The attendee attribute of
an entry has been extended to fit the ATTENDEE
property in iCal.
Group Scheduling data storage – Entries can now hold
properties defined in RFC2445 such as the unique
identifier (UID),
sequence number (SEQUENCE
), Recurrence ID
(RECURRENCE-ID
), repeating events defined by the
RDATE
property, local time zone rules and the METHOD
property.
Group scheduling entries that act on the same event can be a stored and retrieved in an associative manner by means of a common UID.
New Observer Features - The user now can start/stop to observe the changes in the file made by other clients
Calendar alarms will be managed in the Calendar server without interaction from clients
No support for Todo Lists - Todo entries no longer belong to a specific Todo list.
The changes above have introduced a break to both source and binary compatibility. Hence, API users will have to make modifications to their code.
Connecting to the Agenda Model could be done in a number of ways. Its APIs still support the notion of single client operation even though changes have long since been made to make it a multi-client server.
Connection to the calendar server has been simplified and is now
handled by a single class called CCalSession
.
The class CCalSession
is the interface to the
Calendar file. The instantiation of CCalSession
will result in a
connection to the Calendar server.
The CCalSession
API is also used to perform all calendar
file operations (creation, listing, opening, etc.).
When the file is opened, the application must use the session as a handle to create other calendar class instances such as
views (see the Entry and Instance Views section)
iterators (see the Iterator section)
category managers (see the Category Support section)
data exchange objects (see the Importing and Exporting section)
observers (see the CallBack Interface section).
Although it is possible to create multiple instances of the
CCalSession
class per application, this practice is not
recommended. The CCalSession
class is used to manage the
connection to not only the calendar server but also other services such as time
conversion which are required to make use of the additional features
implemented for iCalendar support. Creating more than one session will result
in unnecessary resources being created for the application.
Applications should also endeavour to create the
CCalSession
at start-up and destroy it only when the application
terminates.
//Instantiation
IMPORT_C static CCalSession* NewL();
IMPORT_C ~CCalSession();
//File Access
IMPORT_C void CreateCalFileL(const TDesC&aFileName) const;
IMPORT_C void OpenL(const TDesC& aFileName) const;
IMPORT_C const TDesC&DefaultFileNameL() const;
IMPORT_C void DeleteCalFileL(const TDesC& aFileName) const;
IMPORT_C CDesCArray* ListCalFilesL() const;
//Observation Queries
IMPORT_C void StartChangeNotification(MCalChangeCallBack* aCallBack, CalChangeCallBack::TChangeEntryType aChangeEntryType, TBool aIncludeUndatedTodos, TCalTime aFilterStartTime, TCalTime aFilterEndTime);
IMPORT_C void StartChangeNotification(MCalChangeCallBack2& aCallBack, const CCalChangeNotificationFilter& aFilter);
IMPORT_C void StopChangeNotification();
IMPORT_C void DisableChangeBroadcast();
IMPORT_C void EnableChangeBroadcast();
IMPORT_C void FileIdL(TCalFileId& aCalFileId) const;
IMPORT_C void GetFileNameL(TCalPubSubData aPubSubData, TDes& aFileName) const;
IMPORT_C TBool IsFileNameL(TCalPubSubData aPubSubData, const TDesC& aFileName) const;
IMPORT_C TBool IsOpenedFileL(TCalPubSubData aPubSubData) const;
The client should pay attention to the follow points:
Before using any of the classes exported by the Calendar API clients must create a session object. Some of the classes (particularly those that handle calendar time) rely on the presence of this session to manage resources. Using classes without the presence of a session object in the application could result in functions leaving unexpectedly.
As mentioned earlier, the session object is required as a handle when creating other calendar objects. Therefore, the session object must not be destroyed until all the objects which where created by referencing it have been destroyed.
When CCalSession::OpenL()
is called again
with a new file name, the current file will be closed. Consequentially, all
objects which have a handle to should be deleted since the session no longer
holds the same file.
The application can choose to start/stop observer notifications. It is also possible to choose the type (interested entry type) and the scope (interested time range) to observe. Observers are covered in more detail in the CallBack Interface section.
Accessing calendar entries and all their associated data using the Agenda Model API could be done using a number of classes. The choice was based to a certain extent on the type/amount of data required as well as on the legacy design of the API.
The CalInterimAPI provides the view classes which are
the only means of retrieving calendar entries. The most significant change is
the removal of entry identifiers. Whilst referring to entries with an
identifier that was unique to the file was possible in the past, this is no
longer the case. The only means of identifying entries is now using the
iCalendar properties of UID
and RECURRENCE-ID
.
The term 'view' is used to describe how the user perceives the calendar store. The CalInterimAPI has two different view types. The first, the Entry View, allows the user to use the calendar entries directly. The second, the Instance View, allows the user a higher-level view of the calendar store and works with individual instances of an entry within specified date ranges.
The concept of entries and instances remains fundamentally the same between the Agenda Model and the CalInterimAPI.
An Entry defines a Calendar event of some sort, such as a simple “once-only” to-do reminder, an anniversary (that occurs once each year), or a more complex series of occurrences that have a repeating nature. An entry that defines repeat occurrences does so by containing a Repeat Rule. Apart from some internal structural changes, the basic implementation of the repeat rule in the CalInterimAPI is the same as it was in the Agenda Model (see the Calendar Entry and Instance section for details).
The repeat rule of an entry gives rise to one or more occurrences of the event (also described as a “schedule” in this document). An individual occurrence is known as an Instance.
The entry view (CCalEntryView
) is used to access
and manipulate Agenda Model entries. The instance view
(CCalInstanceView
) is used to access and delete individual
instance(s) generated from an Entry.
An event can result in one or more instances. Instances for an event
can actually originate from more than one entry but these entries will all have
the same UID
. The union of the repeat rules and exception dates
for all the event entries gives the set of event instances.
At this point it is important to describe some differences between the Agenda Model and the new CalInterimAPI in terms of Entry relationships. In the old model, none of the entries were associated to each other. In the new model, associations may exist between entries as explained below.
An exception refers to an occurrence in the original schedule that has been removed and may be replaced with a different occurrence.
In the old model, the replacing occurrence was implemented as a new and separate entry to the original. The new entry defined a single occurrence that replaced one from the original entry’s schedule, however the new entry was not programmatically associated with the original entry.
In the new model, the replacing entry can and should be created
as an iCal entry
using the CCalEntryView::StoreL()
function (see the
Storing CCalEntry Objects section and the
Using the CCalEntryView API for storing iCalendar Entries section). Using this
function requires the creation of a CCalEntry
object that
will define the replacement date (the date specified by the recurrence-id), and
which contains the same UID as the original entry. It results in the replacing
entry (known as the Modifying entry in this document as explained in the
Using the CCalEntryView API for storing iCalendar Entries section) being
explicitly associated with the original entry via its UID.
Note: If the requirement is to merely cancel an individual
occurrence or set of individual occurrences on the original schedule without
replacing them, the CCalEntry::SetExceptionDatesL()
function on the CCalEntry
class can be used.
They can be used to add one-off occurrences to an entry’s
already established repeat rule. They can also be used independently of a
repeat rule to define a completely random series of dates for an entry. The
Agenda Model did not support individual recurrence dates. The CalInterimAPI
provides an API on the CCalEntry
class
(CCalEntry::SetRDatesL()
, see the
Calendar Entry and Instance section) to add
RDates. This does
not result in the creation of a new entry.
As explained in detail in the
Using the CCalEntryView API for storing iCalendar Entries section, the new
CalInterimAPI enables iCal type entries to be stored. The entries can contain
properties including repeat rules (RRules). To make changes to an established
RRule the client should use the CCalEntryView::StoreL()
function to add a modifying entry to an
originating entry
(see the Using the CCalEntryView API for storing iCalendar Entries section). These
entries then become explicitly associated with each other.
To access the entries in a calendar file, clients should use classes
CCalEntryView
or CCalInstanceView
to
process the entry data. As mentioned earlier, both view classes require the
handle to the file which is represented by class
CCalSession
.
The entry view can be used for adding, deleting, updating and searching entries; the instance view can only be used for searching and deleting instances of entries.
Note that if all instances with the same UID are deleted, the originating entries will automatically get deleted.
The CCalEntryView
class offers APIs to store,
fetch and delete CCalEntry
objects. These APIs should be
used instead of the previous ones offered by the CAgnEntryModel
class.
The APIs are listed below:
static CCalEntryView* NewL(CCalSession& aSession, MCalProgressCallBack& aProgressCallBack);
~CCalEntryView();
void FetchL(const TDesC8& aUid, RPointerArray<CCalEntry>& aCalEntryArray) const;
CCalEntry* FetchL(TCalLocalUid aId) const;
void GetIdsModifiedSinceDateL(const TCalTime& aTime, RArray<TCalLocalUid>& aIds) const;
void StoreL(const RPointerArray<CCalEntry>& aCalEntryList, TInt& aNumSuccessfulEntry);
void UpdateL(const RPointerArray<CCalEntry>& aCalEntryList, TInt& aNumSuccessfulEntry);
void DeleteL(const CDesC8Array& aUidList);
void DeleteL(const CCalEntry& aCalEntry);
void DeleteL(const CalCommon::TCalTimeRange& aCalTimeRange, CalCommon::TCalViewFilter aFilter, MCalProgressCallBack& aProgressCallBack);
void DeleteL(const RArray<TCalLocalUid>& aIds, TInt& aNumSuccessfulDeleted);
For comparison purposes the table below lists the new APIs against the old ones. Note however that in almost all cases the behaviour has changed.
|
The CCalEntryView::NewL()
function is used to
create an entry view object.
CCalEntryView* CCalEntryView::NewL(CCalSession& aSession, MCalProgressCallBack& aProgressCallBack);
Note that in addition to the session object,
aProgressCallBack
must be provided to the function (see the
Importing and Exporting section).
If this is the first time a view is being created from a
CCalSession
instance, the call will take longer than any
subsequent calls. This is due to processing that must be done on the server to
prepare entry indexes.
The application should not call any other functions before the
callback function MCalProgressCallBack::Completed()
is
completed.
The CCalEntryView::StoreL()
function is used
to store one or many CCalEntry
objects (for details on the
CCalEntry
class refer to the
Calendar Entry and Instance section).
void StoreL(const RPointerArray<CCalEntry>& aCalEntryList, TInt& aNumSuccessfulEntry);
where aCalEntryList
references one or more
CCalEntry
objects and aNumSuccessfulEntry
will
indicate the number of entries successfully stored on return.
The CCalEntryView::UpdateL()
function is used
to update parameters of entries that already exist in the store (i.e. ones that
have been stored previously).
void UpdateL(const RPointerArray<CCalEntry>& aCalEntryList, TInt& aNumSuccessfulEntry);
where aCalEntryList
references one or more
CCalEntry
objects that contain the updated data. The
CCalEntryView::FetchL()
function can be used to retrieve
those entries required to be updated. The
instances may
then be updated and re-stored using UpdateL()
.
It is important to review the
Using the CCalEntryView API for storing iCalendar Entries section to understand
how to successfully use the UpdateL()
function in the Group
Scheduling context.
Note that:
The UpdateL()
function can only be used to update
an originating CCalEntry
entry (i.e. the initial entry that first
defined the event). If the entry is a modifying entry (one that modifies the
schedule of an originating entry, see the
Modifying Entry section), a Leave
will occur.
The UpdateL()
function should only be used to
update non scheduling properties such as Attendee list, Owner, Alarm, and
Status. Properties that do relate to scheduling dates/times such as an
entry’s RRule,
RDate,
Exception date or
TZRule should NOT be updated using UpdateL()
. See the
Using CCalEntryView::UpdateL() section for more information.
There are two overloaded
CCalEntryView::FetchL()
functions which are used to fetch
CCalEntry
objects.
void FetchL(const TDesC8& aUid, RPointerArray<CCalEntry>& aCalEntryArray) const;
CCalEntry
objects that have the same UID relate to
the same Event, and normally consist of one originating entry and zero or more
modifying entries. The originating entry is the first element in the array.
CCalEntry* FetchL(TCalLocalUid aId) const;
This function is used to fetch a single CCallEntry
based on its local unique id.
There is also another function for retrieving entries from the calendar store.
void GetIdsModifiedSinceDateL(const TCalTime& aTime, RArray<TCalLocalUid>& aIds) const;
This function will get all the entry ids for entries that have
been modified since ‘aTime
’.
There are four overloaded
CCalEntryView::DeleteL()
functions which are used to
delete existing entries from the store.
void DeleteL(const CDesC8Array& aUidList);
Deletes all entries with the same UIDs. Note that ALL entries
(originating and modifying) that have any of the UIDs given by
aUidList
will be deleted.
void DeleteL(const CCalEntry& aCalEntry);
Deletes the specified entry from the store. If the entry being deleted is an originating entry, any modifying entries (i.e. entries with the same UID as the originating) will also be deleted.
void DeleteL(const CalCommon::TCalTimeRange& aCalTimeRange, CalCommon::TCalViewFilter aFilter, MCalProgressCallBack& aProgressCallBack);
Deletes entries which occur within the time range specified by
aCalTimeRange
and pass the filter given by aFilter
.
Note that if an originating entry is deleted, the modifying entries will also
be deleted.
void DeleteL(const RArray<TCalLocalUid>& aIds, TInt& aNumSuccessfulDeleted);
Deletes the entries that have the local unique ids in the array
aIds
.
The situation is similar to creating an entry view object, i.e. the
server will build its indexes when an instance view is instantiated if they
have not been built already. It is essential to make sure that the callback
completion function has been called before using the
CCalInstanceView
APIs.
An instance could be a single calendar entry or a repeated instance of a calendar entry. The instance view can be used to get or delete instances. The APIs are listed as below:
IMPORT_C static CCalInstanceView* NewL(CCalSession& aSession, MCalProgressCallBack& aProgressCallBack);
IMPORT_C ~CCalInstanceView();
IMPORT_C void DeleteL(CCalInstance* aInstance, CalCommon::TRecurrenceRange aWhichInstances);
IMPORT_C void FindInstanceL (RPointerArray<CCalInstance>& aInstanceList, CalCommon::TCalViewFilter aCalViewFilter, const CalCommon::TCalTimeRange& aTimeRange) const;
IMPORT_C void FindInstanceL(RPointerArray<CCalInstance>& aMatchedInstanceList, CalCommon::TCalViewFilter aCalViewFilter, const CalCommon::TCalTimeRange& aTimeRange, const TCalSearchParams& aSearchParams) const;
IMPORT_C TCalTime NextInstanceL(CalCommon::TCalViewFilter aCalViewFilter, const TCalTime& aStartDate) const;
IMPORT_C TCalTime PreviousInstanceL(CalCommon::TCalViewFilter aCalViewFilter, const TCalTime& aStartDate) const;
Note that:
TAgnInstanceId
was used to identify a particular
occurrence of an entry in the Agenda Model. This id has been hidden from the
client now. Instead, the user uses a data time range to specify the instances
of interest.
CCalInstanceView::DeleteL
function deletes
one or more instances of an entry from the model. It is equivalent to the old
API CAgnModel::DeleteInstanceL()
.
CCalInstanceView::FindInstanceL()
function
(the first one) gets all instances found in the specified date range, filtered
as required. It replaces the old API in the Agenda Model:
CAgnModel::PopulateDayInstanceListL()
CAgnModel::PopulateDayDateTimeInstanceListL()
CAgnModel::PopulateMonthInstanceListL()
CCalInstanceView::FindInstanceL()
function
(the second one) gets all instances found in the specified date range, filtered
as required, with the specified text. It replaces two old APIs:
CAgnModel::FindNextInstanceL()
CAgnModel::FindPreviousInstanceL()
The last two APIs are equivalent to the old APIs
CAgnModel::NextDayWithInstance()
CAgnModel::PreviousDayWithInstance()
For the four search APIs, the time property of the instance on which the search is based is the start time for non-Todo entries and the end time for Todo entries.
The CAgnEntry
is replaced with
CCalEntry
which contains Group Scheduling properties. The
class hierarchy to present different types of entries has been removed. The
class CCalEntry
entry still has 5 different types:
Appointment
All day event (doesn’t has a specific start/end time)
Todo entry
Reminder (has a start time but not end time)
Anniversary
The client can set and get the type. An entry might or might not have
a repeating rule.
A new class CCalInstance
has been introduced to represent
an occurrence of a repeating entry. All entry attributes are stored in
CCalEntry
while CCalInstance
has a pointer to the
entry data and have a date/time attribute which indicates the date when the
instance occurs.
The client can get and set the local unique id
(TCalLocalUid
) of an entry, but there are a few
limitations. The local unique id of an entry is created by the agenda server
when that entry is first added to the agenda file and will remain unchanged for
the lifetime of that entry. The client should only set the local unique id on
an entry if it is setting it to be the same as an entry which it is going to
replace.
The APIs can be found in the header file calentry.h
and
calInstance.h
. Comparing with the CAgnEntry
, the
CCalEntry
has the following features:
There is no hierarchy in the entry class –
CCalEntry
is the only class which presents a Calendar entry
object.
Regardless of whether an iCalender Group Scheduling entry is to be created, the new API requires that a UID be specified.
CCalUser
class is introduced to cater for
the new ORGANIZER
property that is introduced in iCalender.
Furthermore, this is derived into a CCalAttendee
class
which encapsulates the ATTENDEE
property. The user can get/set
organizer, phone owner and attendees.
A new class CCalInstance
has been introduced
to play a similar role of TAgnInstanceId
. The APIs are:
IMPORT_C ~CCalInstance();
IMPORT_C CCalEntry& Entry() const;
IMPORT_C TCalTime Time() const;
IMPORT_C TCalTime StartTimeL() const;
IMPORT_C TCalTime EndTimeL() const;
Application programmers should be aware that although it is possible
to get instances, the only way instances can be created is to create or modify
an entry. With the instance object retrieved the application can find the
instance start time and end time using
CCalInstance::StartTimeL()
and
CCalInstance::EndTimeL()
. The
CCalInstance::Time()
function will return the start time if
it is an instance that relates to an event entry, but will return the end time
if it is the instance of a to-do entry. Other attributes can be found from the
entry which the instance belongs to.
Many of the functions on CCalEntry
map onto
functions in the Agenda Model API. There are also new functions to implement
Group Scheduling properties. The APIs which are specifically for Todo entries
are not included in this section.
|
Start and end dates are also very similar, but it varies depending on type.
|
In comparison with the Agenda Model, the APIs of Todo entries have the following changes:
A Todo entry is no longer attached to a specific Todo list.
The end time of Todo entries (which is used as the due date) is used to retrieve entries (See Note 6 in the Class CCalInstanceView API List section)
The APIs which are specifically for Todo entries are compared in the table below:
All Todo alarms are to be set prior to the end time.
|
The attendee class CCalAttendee
is used to
encapsulate all the properties of a meeting attendee that could potentially be
required by group scheduling. It improves upon the Agenda Model
CAgnAttendee
class by providing storage for additional attributes
specified in iCalendar.
The attributes ROLE
and STATUS
may only hold
values that comply with the iCalendar standard as detailed in the tables below.
Support has still been retained for exporting of Attendee properties using vCalendar version 1.0. Due to the difference in parameter values between attendees in vCalendar and iCalendar, a parameter mapping has been introduced and is covered in the sections that follow.
The iCalendar role and status values will be stored as
X-ROLE
and X-STATUS
to preserve vCalendar information
during import and export. However, since there is no direct correlation between
the vCalendar and iCalendar role/status values there is no 1-1 mapping when
changing/setting the role/status. Below is tabled the mapping which occurs when
setting an attendees role/status using either the iCalendar or vCalendar
role/status.
Setting the vCalendar Role:
|
An ORGANIZER
is no longer a value taken by an
ATTENDEE
property but is a separate property in itself. Thus an
attendee with a role of an organizer in vCalendar will not add or set any
values to the iCalendar role during import.
The organizer is a required property in a group scheduled entry and
is represented by the CCalUser
class.
Setting the iCalendar role:
|
The status values that are permitted in iCalendar are for the most
part the same as those in vCalendar with the addition of an
IN-PROCESS
value and the removal of RECEIVED
and
SENT
values.
Setting the vCalendar Status:
|
Setting the iCalendar Status:
|
Two new descriptor attributes have been added to the attendee class
to store the common name (CN
) and sent-by (SENT-BY
)
fields in the iCalendar specification. The sent-by attribute can only be set
during the construction of the CCalAttendee
object by
calling:
IMPORT_C static CCalUser* NewL(const TDesC& aAddress, const TDesC& aSentBy);
whilst the common name can be set any time after construction by calling:
IMPORT_C void SetCommonNameL(const TDesC& aCommonName)
It is still necessary to include an address field when constructing an attendee.
IMPORT_C static CCalAttendee* NewL(const TDesC& aAddress);
IMPORT_C static CCalAttendee* NewL(const TDesC& aAddress, const TDesC& aSentBy);
An entry can optionally have one phone owner and one organizer, which
are both of class CCalUser
. The organizer may or may not
be an attendee of that entry, but the phone owner must be an attendee.
The new class TCalRRule
is used to handle
repeating rules. There are 4 basic types of repeating patterns, they are:
daily, weekly, monthly and yearly.
The API is as follows:
IMPORT_C TCalRRule();
IMPORT_C TCalRRule(TType aType);
IMPORT_C void SetType(TType aType);
IMPORT_C TType Type() const;
IMPORT_C void SetDtStart(const TCalTime& aTime);
IMPORT_C TCalTime DtStart() const;
IMPORT_C void SetUntil(const TCalTime& aTime);
IMPORT_C TCalTime Until() const;
IMPORT_C void SetCount(TUint aCount);
IMPORT_C TUint Count() const;
IMPORT_C void SetInterval(TInt aInterval);
IMPORT_C TInt Interval() const;
IMPORT_C void SetByDay(const RArray<TDay>& aDays);
IMPORT_C void GetByDayL(RArray<TDay>& aDays) const;
IMPORT_C void SetByDay(const RArray<TDayOfMonth>& aDays);
IMPORT_C void GetByDayL(RArray<TDayOfMonth>& aDays) const;
IMPORT_C void SetByMonthDay(const RArray<TInt>& aMonthDays);
IMPORT_C void GetByMonthDayL(RArray<TInt>& aMonthDays) const;
IMPORT_C void SetByMonth(const RArray<TMonth> aMonths);
IMPORT_C void GetByMonthL(RArray<TMonth>& aMonths) const;
IMPORT_C void SetWkSt(TDay aDay);
IMPORT_C TDay WkSt() const;
This interface is intended to map closely to iCalendar, and there are set and get functions for a number of iCalendar properties.
Some of the functions still map directly onto functions in the Agenda
Model repeat rule class, TAgnRpt
:
|
In the iCalendar specification, only one of the Until
or
Count
properties may be set. This is reflected in
TCalRRule
, where only one of the two values is stored. If
the Count
is set, then the Until
date is reset. If
the Until
is set, the Count
is reset.
The exact end date will be calculated once the repeat rule is set on an
entry. This also means that there is no longer a function to find the end date
given a certain number of instances, as FindRptEndDate(TUint
aInstanceCount)
used to do. TCalRRule::SetCount()
is the equivalent of calling FindRptEndDate()
then passing the
result into SetEndDate()
.
Note also that when a repeat rule is retrieved from a
CCalEntry
, it is possible to get both the
Count
and the Until
properties.
The Agenda Model implemented different rule types by deriving classes
from TAgnRpt
, however there is only one class in
TCalRRule
, its type can be set using
TCalRRule::SetType()
or by passing in the
TType
in the constructor. The type may only be set once,
after that it cannot be changed.
Here are examples of each of the repeat rule types, how they might be implemented in the old API and how to implement them using the new API. Note that setting the start date, end date and interval has been omitted because it is straightforward in both APIs (see the above table).
For example, a rule repeating every week on Tuesday and Thursday.
TAgnWeeklyRpt weekly;
weekly.SetDay(ETuesday);
weekly.SetDay(EThursday);
In the new API an array is used to store the weekdays:
TCalRRule weekly(TCalRRule::EWeekly);
RArray<TDay> dayArray;
CleanupClosePushL(dayArray);
dayArray.AppendL(ETuesday);
dayArray.AppendL(EThursday);
weekly.SetByDay(dayArray);
CleanupStack::PopAndDestroy(&dayArray);
For example, a rule repeating every month on the 3rd and the 20th. (These dates are represented as 2 and 19, as in a TDateTime).
TAgnMonthlyByDatesRpt monthlyByDate;
monthlyByDate.SetDate(2);
monthlyByDate.SetDate(19);
In the new API an array is used.
TCalRRule monthlyByDate(TCalRRule::EMonthly);
RArray<TInt> dateArray;
CleanupClosePushL(dateArray);
dateArray.AppendL(2);
dateArray.AppendL(19);
monthlyByDate.SetByMonthDay(dateArray);
CleanupStack::PopAndDestroy(&dateArray);
For example, a rule repeating every month on the 1st Wednesday and the last Friday.
TAgnMonthlyByDaysRpt monthlyByDay;
monthlyByDay.SetDay(EWednesday, TAgnRpt::EFirst);
monthlyByDay.SetDay(EFriday, TAgnRpt::ELast);
In the new API an array is used.
TCalRRule monthlyByDay(TCalRRule::EMonthly);
RArray<TCalRRule::TDayOfMonth> dayArray;
CleanupClosePushL(dayArray);
TCalRRule::TDayOfMonth dayOfMonth1(EWednesday, 1); // 1 represents the first week
dayArray.AppendL(dayOfMonth1);
TCalRRule::TDayOfMonth dayOfMonth2(EFriday, -1); // -1 represents the last week
dayArray.AppendL(dayOfMonth2);
monthlyByDay.SetByDay(dayArray);
CleanupStack::PopAndDestroy(&dayArray);
In both the old API and the new API the repeat date is taken from the start date of the repeat rule.
For example, every year on the last Saturday of July.
TAgnYearlyByDayRpt yearlyByDay;
yearlyByDay.SetStartDay(ESaturday, TAgnRpt::ELast, EJuly, yearNumber);
In the new API two arrays are used to set the day of the month and the month.
TCalRRule yearlyByDay(TCalRRule::EYearly);
RArray<TCalRRule::TDayOfMonth> dayArray;
CleanupClosePushL(dayArray);
TCalRRule::TDayOfMonth dayOfMonth1(ESaturday, -1); // -1 represents the last week
dayArray.AppendL(dayOfMonth1);
yearlyByDay.SetByDay(dayArray);
CleanupStack::PopAndDestroy(&dayArray);
RArray<TMonth> monthArray;
CleanupClosePushL(monthArray);
monthArray.AppendL(EJuly);
yearlyByDay.SetByMonth(monthArray);
CleanupStack::PopAndDestroy(&monthArray);
To store a repeat rule in an entry using the new API, use CCalEntry::SetRRuleL(const TCalRRule& aRule).
The iterator class CCalIter
is used to iterate
through all calendar entries and retrieve the UIDs of the entries. If a
calendar event is composed of more than one entry (this is the case when
entries are updated in group scheduling), the iterator returns the originating
entry only and does not return its modifying entries. Should the modifying
entries be required the CCalEntryView::FetchL()
function
can be used.
The CCalIter
class is also created with a handle to
CCalSession
.
The Iterator may be used, for example, by a synchronisation application to iterate all entries in the file to find entries that have been added/deleted between two synchronisations. The APIs are defined as follows:
IMPORT_C static CCalIter* NewL(CCalSession& aSession);
IMPORT_C ~CCalIter();
IMPORT_C const TDesC8& FirstL();
IMPORT_C const TDesC8& NextL();
Note that:
CCalIter::FirstL()
sets the iterator to first
entry in the calendar database and returns its UID. KNullDesC8
will be returned if the file is empty
CCalIter::NextL()
sets the iterator to the
next entry in the calendar database and returns its UID.
KNullDesC8
will be returned if there are no more entries found in
the file. However, if the call to FirstL()
returned
KNullDesC8
then NextL()
will leave if it is called.
The UID retrieved can be used to fetch all associated modifying
entries using CCalEntryView::FetchL()
Attributes of the entry can be found through the
CCalEntry
functions
The count function is not supported in this release due to complications with the current database implementation (based on the Agenda Model).
The class CCalCategoryManager
is used to carry out
category operations. The class also needs a handle to
CCalSession
. The category type and functionality supported
in the Interim API are very similar to functions in CAgnModel
.
|
The CCalCategoryManager::FilterCategoryL()
and
CCalCategoryManager::DeleteCategoryL()
functions take a
MCalProgressCallBack
parameter. This allows a callback
when the filtering/deleting operation is completed.
The class CCalDataExchange
is used to encapsulate
the operations of importing/exporting. The functionality has been extended from
the Agenda Model to include the import/export of the iCalendar data described
in this document.
The new APIs are:
IMPORT_C static CCalDataExchange* NewL(CCalSession& aSession);
IMPORT_C ~CCalDataExchange();
IMPORT_C void ImportL(TUid aDataFormat, RReadStream& aReadStream, RPointerArray<CCalEntry>& aCalEntryArray);
IMPORT_C void ExportL(TUid aDataFormat, RWriteStream& aWriteStream, RPointerArray<CCalEntry>& aCalEntryArray);
The parameter “aDataFormat
” refers to the
data format of the Import or Export. Currently only the format
KUidVCalendar
(defined in CalDataFormat.h
) is
supported. This will export/import Entries using the vCalendar 1.0 standard.
There are two callback Interfaces:
class MCalChangeCallBack
virtual void Progress(TInt aPercentageCompleted) = 0;
virtual void Completed(TInt aError) = 0;
virtual TBool NotifyProgress() = 0;
This class is used to observe the entry changes in the Calendar file (see the CCalSession - Connecting to the Calendar Server section).
It is used in
CCalSession::StartChangeNotification()
.
class MCalProgressCallBack
virtual void CalChangeNotification(TChangeEntryType aChangeEntryType) = 0;
This class is used to get progress and completion notification when
the operation is asynchronous. The user can pass the same or a different
callback object to those functions which require
MCalProgressCallBack
as a parameter. However, starting another
asynchronous task before the previous one completes is not permitted.
It is used in
The Agenda Model provided two mechanisms for an application to obtain
notification of changes made by other applications. The first mechanism was to
use the function RAgendaServ::StartNotifierL()
which caused the
Agenda server to be polled periodically to determine whether a change had
occurred in the Agenda Model and a callback function supplied by the client was
called whenever a change was detected. However, the implementation of this
mechanism was imperfect and it was possible for notifications to be missed
under some circumstances.
The second mechanism involved the client implementing an ECOM observer
plug-in which was instantiated within the agenda server process. Observers
implemented the virtual interface defined by CAgnObserver2
. This
mechanism was also problematic and was not in general use.
The CalInterimAPI also provides two mechanisms for an application to obtain notification of changes made by other applications. The first mechanism is for clients that have a continuous open connection to the calendar file. The second is a publish and subscribe mechanism which is designed for use with clients that are not connected to the calendar file, but still need to be notified of changes to that file.
The connected client change notification mechanism is targeted at
UI applications that need to update their display of the calendaring data when
other applications make changes to the data store. The CalInterimAPI provides
connected clients with a notification mechanism whereby clients implement an
interface defined by the abstract class
MCalChangeCallBack2
which has the following function
member:
void CalChangeNotification(RArray<TCalChangeEntry>& aChangeItems)
This member function is called whenever there are changes to the Agenda Model that match the specified filter criteria.
The aChangeItems
parameter is a list changes since the
last notification. The change information is conveyed via a
TCalChangeEntry
class that contains the local uid of the
changed entry, the type of entry it is (Todo or Event) and the type of change
that it was (add, modify or delete).
A client requests notification of changes using the function
void CCalSession::StartChangeNotification(MCalChangeCallBack2& aCallBack, const CCalChangeNotificationFilter& aFilter);
This has the following parameters:
aCallback
: Specifies the instance of the
implementation of the interface to call when a change occurs
aFilter
: Specifies the filter that is used when
notifying the client of changes to the agenda model.
The filter is defined using the class
CCalChangeNotificationFilter
which is constructed with a
NewL()
that takes the filter details described below.
aChangeEntryType
: Specifies if the observer is
interested in events to-dos or both.
aIncludeUndatedTodos
: Specifies whether the client
should be notified of changes to undated Todo entries, this parameter only has
meaning if aChangeEntryType
included Todo entries
aTimeRange
: Clients receive notification for
entries with a start date that fall within this date range, if an entry is
modified and the start date of the entry prior to the modification was outside
the date range or vice versa then client is notified of the change to the
entry.
A client can disable change notification using
CCalSession::StopChangeNotification()
.
Change notifications are buffered on the server and clients may receive notification of more than one change at a time.
A client may wish to make a large number of successive changes to the Calendaring data store, in this situation, it would not be appropriate to notify other clients of these changes since it may generate many successive updates their display of the data. In this use case, API functions are provided to disable the notification of changes to other clients for the duration of the bulk operation. This is done with the following functions:
IMPORT_C void CCalSession::DisableChangeBroadcast();
IMPORT_C void CCalSession::EnableChangeBroadcast();
After a client re-enables change notification (using
CCalSession::EnableChangeBroadcast()
) then all other
clients will receive a notification that an undefined change has occurred.
Unconnected clients may also receive notifications of changes to a
particular calendar file. This is done by registering with the ‘publish
and subscribe’ notification category
KCalPubSubCategory
.
When a notification is received (i.e. the RunL()
of the
observer is called) the client will need to get the data which will be a
TCalPubSubData
.
This can then be used with the functions below.
void CCalSession::GetFileNameL(TCalPubSubData aPubSubData, TDes& aFileName) const;
On return aFilename
will be the name of the agenda
file that contains the change.
TBool CCalSession::IsFileNameL(TCalPubSubData aPubSubData, const TDesC& aFileName) const;
This tests whether the file name passed is the agenda file that contains the change.
TBool CCalSession::IsOpenedFileL(TCalPubSubData aPubSubData) const;
This tests if the change is to the file that is currently open and can be used in the case where the client is actually connected to the file, but is still using publish and subscribe notifications.
Note that these functions are listed in ascending order of efficiency (i.e. the latter should be used in preference if possible).
The client can find the Local UIDs of the entries that have changed by calling:
void CCalEntryView::GetIdsModifiedSinceDateL(const TCalTime& aTime, RArray<TCalLocalUid>& aIds) const;
The client should pass in the time ‘aTime
’
that they last had a valid view of the agenda file (i.e. the last time they
received a notification). On return ‘aIds
’ contains
a list of all the local entry uids that have changed.
The TCalPubSubData
from the notification also
includes the time that the change happened so that the client can test if that
changed happened before or after the time that they last had a valid view.
Since neither of the existing change notification mechanisms available in the Agenda Model are in general use, there should be no migration issues.
The Calendar Store can store CCalEntry
objects
that hold iCal properties (as defined in
RFC2445). The store
can associate entries that are supplied with the same UID such that the
associated entries apply to the same event (VEVENT
,
VTODO
, etc.).
An “originating” entry can define a set of recurrence rules for the entire event. Additional “modifying” entries can then be added which define modifications to the original recurrence pattern. Since the store can maintain a set of associated entries, all applying to the same event, it is the list of associated entries that combine to define the event’s total schedule in terms of occurrence dates and times.
It is envisioned that the user of the CalIntermAPI is an iCal-intelligent application that understands and implements logic defined in both the RFC2445 and the RFC2446 specification. In particular the client understands the relationships between the RecurrenceId, Sequence Number and UID.
The role of the CalInterimAPI is primarily to provide access to a store
(the Agenda Model Store) and the ability to create, delete and modify
CCalEntry
objects. The API has no inherent knowledge of RFC2446,
and as such it is up to the client to interpret iCal requests it receives from
other sources, implement iCal logic, and then make appropriate updates to the
store by creating/deleting/fetching/updating the relevant
CCalEntry
objects.
In particular, the API does not process the Sequence Number or Method properties. That is, these values are get and set, but their values are not checked or processed by the CAlInterimApi. The CalInterimApi does however have some inherent behaviour when storing entries with regards to RecurrenceId, UID, scheduling/recurrence properties such as repeat rules (RRule, RDate) and Exception dates which are very important to understand.
If no other entry exists in the store with the same UID, the entry is stored as an originating entry.
If an originating entry contains a RecurrenceID property then this function will leave.
If an entry does already exist in the store with the same UID, and the new entry does not contain a RecurrenceID it will replace the existing entry.
A Modifying entry has the same UID as an Originating entry. It modifies the original recurrence set in some way. It specifies a RecurrenceID and RecurrenceID Range and optionally an RRule.
If the entry contains a RecurrenceID (together with RecurrenceID Range) and an originating entry already exists in the store, the entry is stored as a modifying entry.
If there already exists a modifying entry with the same UID and RecurrenceID (together with RecurrenceID Range), then the existing modifying entry is deleted and the new entry replaces it.
If no such entry exists, the new entry is stored as a modifying entry.
When a modifying entry is stored, the originating entry may end up being modified in the following way:
Non-Repeating Modifying Entry
If the modifying entry does not contain an RRule, the occurrence instance on the original schedule that is specified by the modifying entry’s RecurrenceID will be exceptioned, and the modifying entry’s Start/End date will define the new occurrence instance.
Repeating Modifying Entry
If the modifying entry contains an RRule, the RecurrenceID and RecurrenceID Range are used to trim the originating entries RRule occurrences.
For example if the originating recurrence rule spanned 5th January 2005 to 26th January 2005 with weekly events on each Wednesday, and the modifying entry’s RecurrenceID and RecurrenceID Range are 19th January 2005 and ThisAndFuture respectively, the originating entry’s recurrence rule will be modified such that it ends on the 12th January 2005, thus resulting in the final recurrence set consisting of the union of the originating recurrence rule and the modifying recurrence rule.
A similar modification takes place if the RecurrenceID Range is ThisAndPrior but this time it is the front portion of the originating recurrence rule that gets trimmed.
Furthermore, any Exceptions dates and RDates on the originating entry that fell in the range now trimmed are removed from the entry.
Furthermore, any non-repeating modifying entries that fell in the range now trimmed are deleted from the store.
There can only be one repeating modifying entry per originating
entry. If the user attempts to add a second modifying entry which is different
to the existing modifying entry, then a Leave
will occur. By
‘different’ we mean not having the exact same GUID, RecurrenceID
and RecurrenceID Range values. (A replacing modifying entry
can be legally made as described in the
Modifying Entry section).
A process of trimming occurs on the originating entry’s repeat rule when a repeating modifying entry is stored. This results in a portion of the original recurrence set being lost. It also results in an Exception dates, RDates and non-repeating modifying entries that fell on the portion now lost being removed/deleted. In other words, these occurrences will be lost and will not be shifted by the same delta as the original occurrences appear to be shifted by.
The RecurrenceID Range property, although supplied when the
CCalEntry::NewL()
function is called and used during the
CCalEntryView::StoreL()
function, is not stored.
Modifying entries may contain an RRule but no RDates or Exception
Dates. To add RDates or Exception dates use the functions provided on
CCalEntry
.
If a modifying entry which has a RecurrenceID Range of
ThisAndFuture and a RecurrenceID which selects the first
instance on the originating entry’s schedule is stored using
StoreL()
, a Leave
will occur. If the client’s
intention is to change the entire originating schedule, then this can be
achieved by storing a new originating entry that contains no RecurrenceID;
StoreL()
will first delete the originating entry and then store
the new originating entry in its place. (Note that when the existing entry
becomes deleted, any existing modifying entries will also be deleted).
If a modifying entry which has a RecurrenceID Range of
ThisAndPrior and a RecurrenceID which selects the last
instance on the originating entry’s schedule is stored using
StoreL()
, a Leave
will occur. If the client’s
intention is to change the entire originating schedule, then this can be
achieved by storing a new originating entry that contains no RecurrenceID;
StoreL()
will first delete the originating entry and then store
the new originating entry in its place. (Note that when the existing entry
becomes deleted, any existing modifying entries will also be deleted).
As the above sections show, the
CCalEntryView::StoreL()
function performs several
behind-the-scenes household tasks relating to an event’s recurrence set
when originating and modifying entries are stored. In contrast,
CCalEntryView::UpdateL()
performs none of these tasks. It
is provided as a convenient means to make quick changes to the non-recurrence
type properties of an originating entry. It should only be used to change
properties such as Location, Summary, Description, Attendee list, Organizer,
Owner, etc. It should not be used on modifying entries (a Leave
will occur if this is attempted).
It may be used to change properties such as SequenceNumber, RecurrenceId, Range, etc but doing so may affect the final recurrence set in an adverse way.
It should NOT be used to change the RRule, RDate, Exception date, TZRule properties.
If the originating entry is deleted using any of the overloaded
CCalEntryView::DeleteL()
functions, all associated
modifying entries will also be deleted.
If a non-repeating modifying entry is deleted, the original occurrence that became exceptioned when the modifying entry was stored will remain exceptioned.
The following technical terms and abbreviations are used within this document.
|