You Are Here:

Community: Wiki

This page was last modified 15:31, 24 March 2009.

CalInterimAPI

From Forum Nokia Wiki


The new CalInterim API has been introduced with the Symbian 9.1 releases to replace the old Agenda Model API.

Contents

Connecting

The CCalSession is the interface to the Calendar file. The instantiation of CCalSession will result in a connection to the Calendar Server:

Library reqired:

LIBRARY        calinterimapi.lib

Header files:

#include <calsession.h>
#include <calprogresscallback.h>
#include <caliterator.h>
#include <calentry.h>
#include <calentryview.h>
#include <caltime.h>
#include <calalarm.h>
#include <calcategory.h>
#include <caluser.h>
#include <calrrule.h>
#include <calinstance.h>
#include <calinstanceview.h>
 
 
class CCalendar : public MCalProgressCallBack
    {
    CCalendar();
    void ConstructL();
    ~CCalendar();
    [...]
    private:
    void OpenCalendarL();
    [...]
    CCalSession* 	     iCalSession;
    CCalIter*		     iCalIter;
    CCalEntryView* 	     iCalEntryView;
    HBufC8*		     iNext;
    TBool                    iReady;
    [...]
    }

Source file:

#include "Calendar.h"
 
CCalendar::CCalendar()
    {
    }
 
CCalendar::~CCalendar()
    {
    if(iCalIter)
        {
	delete iCalIter;
	}
    if(iCalEntryView)
	{
	delete iCalEntryView;
	}
    if(iCalSession)
	{
	delete iCalSession;	
	}
    if( iNext )
	{
	delete iNext;
	iNext = NULL;
	}
    }
 
void CCalendar::ConstructL()
    {
    iReady=EFalse; 
    iNext = HBufC8::NewL(50);
    OpenCalendarL();
    }
 
void CCalendar::OpenCalendarL()
    {
    // allocate and construct server
    // Check that calendar exists, and if not, create it.
    // Calendar does not exist until it is created by calendar app - or by us.
    iCalSession = CCalSession::NewL();
    
    //Open the default calendar file
    TRAPD(aErr, iCalSession->OpenL(KNullDesC));
    if(aErr == KErrNotFound)
        {
	TRAPD(err, iCalSession->CreateCalFileL(iCalSession->DefaultFileNameL()) );
	TRAPD(aErr2, iCalSession->OpenL(KNullDesC));
	}
 
    iCalEntryView = CCalEntryView::NewL(*iCalSession, *this);
    iCalIter = CCalIter::NewL(*iCalSession);
 
    //Reset to the begin of the Calendar entries
    TPtr8 aAddress( iNext->Des() );
    aAddress = iCalIter->FirstL();
    }

Do not forget implement other functions!

// Called during calendar entry view creation
void CCalendar::Progress(TInt aPercentageCompleted)
    {
    }
 
void CCalendar::Completed(TInt aError)
    {
    if(aError==KErrNone)
        iReady=ETrue;
    }
 
// Returns whether or not progress notification is required
TBool CCalendar::NotifyProgress()
    {
    // Progress notification is required
    return ETrue;
    }
 
// Utility function to handle CCalEntry insert
// Destroy the RPointerArray
void DestroyRPointerArray(TAny* aPtr)
    {
    RPointerArray<CCalEntry>* self = static_cast<RPointerArray<CCalEntry>*> (aPtr);
    self->ResetAndDestroy();
    }

Insert new Appointment

To insert new item with a given ID you should create a CCalEntry item and fill out with the desidered data.


void CCalendar::NewApptL(TInt aID)
    {
    if(!iReady) //if this function is called too soon then return. Calendar opening 
                //must finish first.
    return;
    
    //Create entry with the given new GUid
    HBufC8* guidBuf;
    TBuf8<30> tmpGuid;
    tmpGuid.Num( aID );
    guidBuf = tmpGuid.AllocL();
 
    CCalEntry* appt = CCalEntry::NewL(CCalEntry::EAppt, guidBuf,  
    CCalEntry::EMethodNone, 0);
    CleanupStack::PushL(appt);
    
    //Some data
    appt->SetSummaryL( _L("Summary") ); 
    appt->SetLocationL( _L("Location") );
    appt->SetDescriptionL( _L("Description") );
 
    //Start / end date
    TTime start;
    start.UniversalTime();
    TTime end;
    end.UniversalTime();
    TCalTime startCalTime;
    startCalTime.SetTimeUtcL(start);
    TCalTime endCalTime; //NullTTime()
    
    //Comment out the next line if you do not want to set end time
    endCalTime.SetTimeUtcL(end);
    
    //Set it
    appt->SetStartAndEndTimeL(startCalTime, endCalTime);
 
    //Store this new Entry
 
    RPointerArray<CCalEntry> entryArray;
    CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &entryArray));
    entryArray.AppendL(appt);
    TInt success(0);
    iCalEntryView->StoreL(entryArray, success);
    entryArray.Reset();
    CleanupStack::PopAndDestroy(&entryArray);
    CleanupStack::PopAndDestroy(appt);
    }

Insert new Todo

To insert new item with a given ID you should create a CCalEntry item and fill out with the desidered data.

void CCalendar::NewTodoL(TInt aID)
    {
    //Create an antry with the given GUid
    HBufC8* guidBuf;
    TBuf8<30> tmpGuid;
    tmpGuid.Num( aID );
    guidBuf = tmpGuid.AllocL();
 
    CCalEntry* todo = CCalEntry::NewL(CCalEntry::ETodo, guidBuf, 
     CCalEntry::EMethodNone, 0);
    CleanupStack::PushL(todo);
 
    todo->SetSummaryL( _L("Summary") ); 
 
    //Priority
    TInt priority = 1;
    todo->SetPriorityL(priority);
 
    //Completed
    TCalTime calTime;
    TTime time; time.UniversalTime();
    calTime.SetTimeUtcL(time);
    todo->SetCompletedL(ETrue, calTime);
 
    //End date
    TTime end;
    end.UniversalTime();
    TCalTime calEndDate;
    calEndDate.SetTimeUtcL(end);
    todo->SetStartAndEndTimeL(calStartDate, calEndDate);
 
    //Store New Entry
    RPointerArray<CCalEntry> entryArray;
    CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &entryArray));
    entryArray.AppendL(aTask);
    TInt success(0);
    iCalEntryView->StoreL(entryArray, success);
    entryArray.Reset();
    CleanupStack::PopAndDestroy(&entryArray);
    CleanupStack::PopAndDestroy(todo);
}

Add old AgendaModel Alarm data to the new CCalEntry Appointment item

This code sample is useful if you port your application from the previous agenda model. In the old model you should use the SetAlarm(TTimeIntervalDays aDaysWarning,TTimeIntervalMinutes aTime). In the new API you can set the alarm only by giving the offset time according to the start date of the entry.

//Get old alarm data
TTimeIntervalDays aDays = ...;
TTimeIntervalMinutes aMinutes= ...;
 
//tmp variables
TInt oneDayInMin = 1440;
TInt64 oneDayInMicroSec = 86400000000LL;
TInt64 oneMinInMicroSec = 60000000;
TCalTime startCalTime;
 
//Set the start time
startCalTime.SetTimeUtcL(start);
TInt offSet =
((startCalTime.TimeLocalL().Int64() % oneDayInMicroSec) / oneMinInMicroSec) -
 aMinutes.Int();
if( offSet < 0 ) offSet = oneDayInMin - Abs(offSet);
TTimeIntervalMinutes tmp( offSet + aDays.Int() * oneDayInMin);
CCalAlarm* alarm = CCalAlarm::NewL();
 
if(alarm)
    {
    alarm->SetTimeOffset(tmp);
    appt->SetAlarmL(alarm);
    delete alarm;
    }

Add old AgendaModel Alarm data to the new CCalEntry Todo item

This lines are similar to the appointment code, but now the alarm offset is calculated from the end date (due date).

//Get old alarm data
TTimeIntervalDays aDays = ...;
TTimeIntervalMinutes aMinutes= ...;
 
//tmp variables
TInt oneDayInMin = 1440;
TInt64 oneDayInMicroSec = 86400000000LL;
TInt64 oneMinInMicroSec = 60000000;
TCalTime endCalTime;
 
//Set end date
endCalTime.SetTimeUtcL(dueDate);
TInt offSet =
((endCalTime.TimeLocalL().Int64() % oneDayInMicroSec) / oneMinInMicroSec) -
 aMinutes.Int();
TTimeIntervalMinutes tmp( offSet + aDays.Int() * oneDayInMin);
CCalAlarm* alarm = CCalAlarm::NewL();
if( alarm )
    {
    alarm->SetTimeOffset(tmp);
    todo->SetAlarmL(alarm);
    delete alarm;
    }

Delete entry

void CCalendar::DeleteItemL(TInt aID)
    {
    CCalEntry* aEntry = NULL;
    TBuf8<30> tmpGuid;
    tmpGuid.Num( aID );
    HBufC8* guidBuf = tmpGuid.AllocL();
    RPointerArray<CCalEntry> tmpArray;
    CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &tmpArray));
    iCalEntryView->FetchL( guidBuf->Des(), tmpArray );
    delete guidBuf;
    
    if( tmpArray.Count() == 0 )
        {
	CleanupStack::PopAndDestroy(&tmpArray);
	return;
	}
    aEntry = tmpArray[0];
    iCalEntryView->DeleteL(*aEntry);
    CleanupStack::PopAndDestroy(&tmpArray);
    }

Update Appointment entry

Because the CCalEntryView->UpdateL() can't update all the attributes of an entry i use a tmpEntry to store the copied data to store the new modified entry, there are some entry swapping to avoid the data loss if a break occour. You can also implement this function using the CopyCalEntryL() function, described at the end of this document.

void CCalendar::UpdateApptL(TInt aID)
    {
    CCalEntry* aAppt = NULL;
    CCalEntry* tmpEntry = NULL;
    TInt aID = GetInt(*aIDText);
    TBuf8<30> tmpGuid;
    tmpGuid.Num(aID);
    HBufC8* guidBuf = tmpGuid.AllocL();
    HBufC8*  guidTmp;
 
    TBool aFound = EFalse;
 
    RPointerArray<CCalEntry> array;
    CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &array));
    TRAPD(aErr, iCalEntryView->FetchL(guidBuf->Des(), array) );
    if (!aErr && array.Count() > 0 )
        {
	aAppt = array[0];
	if( aAppt->EntryTypeL() != CCalEntry::EAppt ) aAppt = NULL;
	aFound = ETrue;
	}
 
    if (aFound)
	{        //This will create new unique UID
	HBufC8* guidTmp = CalenInterimUtils::GlobalUidL();
/*
        //Check the existence of the newly created tmp guid
	TInt itemCount = 0;
	do
	    {
	    TInt guidNum = Math::Random();
	    TBuf8<30> tmpGuid;
	    tmpGuid.Num( Abs(guidNum) );
	    guidTmp = tmpGuid.AllocL();
	    RPointerArray<CCalEntry> tmparray;
	    CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &tmparray));
	    iCalEntryView->FetchL(guidTmp->Des(), tmparray);
            itemCount = tmparray.Count();
	    CleanupStack::PopAndDestroy(&tmparray);
	    if( itemCount > 0 ) delete guidTmp;
	    }
	while( itemCount > 0 );
*/
	tmpEntry = 
	 CCalEntry::NewL(CCalEntry::EAppt, guidTmp, CCalEntry::EMethodNone, 0);
	CleanupStack::PushL(tmpEntry);
	CopyCalEntryL(aAppt, tmpEntry);
	CleanupStack::Pop(tmpEntry);
	}
 
    CleanupStack::PopAndDestroy(&array);
    if( tmpEntry ) CleanupStack::PushL(tmpEntry);
 
    //Set new data:
    tmpEntry->SetSummaryL(_L("Summary")); 
    // [...]
 
    //Store it
 
    if( tmpEntry ) CleanupStack::Pop(tmpEntry);
    if (aFound)
        {
	HBufC8* guidUpdate = guidBuf->AllocL();
	CCalEntry* updateEntry = 
	 CCalEntry::NewL(CCalEntry::EAppt, guidUpdate, CCalEntry::EMethodNone, 0);
	CleanupStack::PushL(updateEntry);
	CopyCalEntryL(tmpEntry, updateEntry);
	CleanupStack::Pop(updateEntry);
 
	HBufC8* deleteGuidTmp = guidTmp->AllocL();
 
	//Insert the tmp entry
	RPointerArray<CCalEntry> insertArray;
	CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &insertArray));
	insertArray.AppendL(tmpEntry);
	TInt success(0);
	iCalEntryView->StoreL(insertArray, success);
	insertArray.Reset();
	CleanupStack::PopAndDestroy(&insertArray);
	delete tmpEntry;
 
	//Delete old
	RPointerArray<CCalEntry> deleteArray;
	CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &deleteArray));
	iCalEntryView->FetchL(guidBuf->Des(), deleteArray);
	CCalEntry* deleteEntry = deleteArray[0];
	iCalEntryView->DeleteL(*deleteEntry);
	CleanupStack::PopAndDestroy(&deleteArray);
 
	//insert new update Entry
	RPointerArray<CCalEntry> updateArray;
	CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &updateArray));
	updateArray.AppendL(updateEntry);
	success = 0;
	iCalEntryView->StoreL(updateArray, success);
	updateArray.Reset();
	CleanupStack::PopAndDestroy(&updateArray);
	delete updateEntry;
 
	//Delete tmp Entry
	RPointerArray<CCalEntry> deleteTmpArray;
	CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &deleteTmpArray));
	iCalEntryView->FetchL(deleteGuidTmp->Des(), deleteTmpArray);
	CCalEntry* deleteTmpEntry = deleteTmpArray[0];
	iCalEntryView->DeleteL(*deleteTmpEntry);
	CleanupStack::PopAndDestroy(&deleteTmpArray);
	delete deleteGuidTmp;
	}
    delete guidBuf;
    }

Update Todo item

The Todo entry update can be made in similar mode than the Appointment entry.

Convert item CCalEntry IDs to old AgendaModel entry IDs

If you are porting your application from the old Agenda API to the new CalInterim API, presumably you have head-ache how to convert the new alphanumeric IDs to the old integer ID-s. Here is an example:

/////////////////////////////////////////////////////////////////////
/*
Because the Global Uid(22 char length &HBufC8) of an entry 
is out of range of TInt32...
This function must be called after the & CCalIter creation
and before any other functions.
*/
 
// Update the Global uids to be in the range of TInt32
void CCalendar::ConvertL()
{
    HBufC8* next = HBufC8::NewL(50);
    TPtr8 aAddress( next->Des() );
    aAddress = iCalIter->FirstL();
 
    while( next->Des() != KNullDesC8 )
        {
        //Checking if the ( UID is string) || (bigger then 2147483646)
	TBool toConvert = EFalse;
	TBuf8<100> des8 = _L8("");
	TRAPD(err_fetch, des8.Copy( next->Des() ) );
	if( err_fetch == KErrNone )
	    {
	    TLex8 lex8 = TLex8(des8);
	    TInt id = 0;
	    TInt err_lex = lex8.Val(id); 
	    if( err_lex != KErrNone ) toConvert = ETrue;
	    }
 
       	//Update
	if( toConvert != EFalse )
            {
	    RPointerArray<CCalEntry> fetchArray;
	    CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, &fetchArray));
	    iCalEntryView->FetchL(next->Des(), fetchArray);
	    CCalEntry* aEntry = fetchArray[0];
 
	    ///////////////////////////////////////////////////
 
 
	    //Convert only the Appontment & Todo
	    if( aEntry->EntryTypeL() == CCalEntry::EAppt || aEntry->EntryTypeL() == 
                  CCalEntry::ETodo )
                {
		//Check the existence of an entry with the new GUid
		HBufC8* guidBuf;
	        TInt itemCount = 0;
		do
		    {
		    TInt guidNum = Math::Random();
	            TBuf8<30> tmpGuid;
		    tmpGuid.Num( Abs(guidNum) );
	            guidBuf = tmpGuid.AllocL();
		    RPointerArray<CCalEntry> tmparray;
		    CleanupStack::PushL(TCleanupItem(DestroyRPointerArray,
                     &tmparray));
		    iCalEntryView->FetchL(guidBuf->Des(), tmparray);
		    itemCount = tmparray.Count();
		    CleanupStack::PopAndDestroy(&tmparray);
		    if( itemCount > 0 ) delete guidBuf;
		    }
		while( itemCount > 0 );
                    CCalEntry* updateEntry;
		    if( aEntry->EntryTypeL() == CCalEntry::EAppt )
		        {
		        updateEntry = 
		        CCalEntry::NewL(CCalEntry::EAppt, guidBuf, 
                        CCalEntry::EMethodNone, 0);
		        }
		    else
		        {
		        updateEntry = 
		        CCalEntry::NewL(CCalEntry::ETodo, guidBuf, 
                        CCalEntry::EMethodNone, 0);
		        }
 
		    //Copy: because updateEntry->CopyFromL( *aEntry, EDontCopyId );    
                    //doesn't work :(
		    CopyCalEntryL(aEntry, updateEntry);
 
		    RPointerArray<CCalEntry> modifyingArray;
		    CleanupStack::PushL(TCleanupItem(DestroyRPointerArray, 
                     &modifyingArray));
		    modifyingArray.AppendL(updateEntry);
		    TInt numberOfEntries(0);
		    iCalEntryView->StoreL(modifyingArray, numberOfEntries);
	            CleanupStack::PopAndDestroy(&modifyingArray);
 
		    //Delete old
		    RPointerArray<CCalEntry> deleteArray;
	            CleanupStack::PushL(TCleanupItem(DestroyRPointerArray,  
                     &deleteArray));
		    iCalEntryView->FetchL(aEntry->UidL(), deleteArray);
		    CCalEntry* deleteEntry = deleteArray[0];
	            iCalEntryView->DeleteL(*deleteEntry);
		    CleanupStack::PopAndDestroy(&deleteArray);
		    }
	        CleanupStack::PopAndDestroy(&fetchArray);
	        }
	    aAddress = iCalIter->NextL();
	    }
        delete next;
	next = NULL;
    }

Copy entry to an other one without Global ID

This function is usefull if CCalEntry CopyFromL( *aEntry, EDontCopyId ) doesn't work. aSource and aTarget should exist before calling this function. All 2 entries should be the same type!

void CCalendar::CopyCalEntryL(CCalEntry* aSource, CCalEntry* aTarget)
    {
    aTarget->SetSummaryL( aSource->SummaryL() );
    aTarget->SetDescriptionL( aSource->DescriptionL() );
    aTarget->SetLastModifiedDateL();
    aTarget->SetDTStampL( aSource->DTStampL() );
    aTarget->SetLocationL( aSource->LocationL() );
    aTarget->SetStatusL( aSource->StatusL() );
    aTarget->SetReplicationStatusL( aSource->ReplicationStatusL() );
    aTarget->SetPriorityL( aSource->PriorityL() );
 
    //On Nokia E60 - sometimes at Todo entries, getting startTime/endTime Leaves!
    //In this case create a new NullTTime Enty
    TCalTime startCalTime;
    TRAPD(err, startCalTime = aSource->StartTimeL());
    if( err != KErrNone )
        {
	TTime time = Time::NullTTime();
	startCalTime.SetTimeUtcL(time);
	}
 
    TCalTime endCalTime;
    TRAPD(err1, endCalTime = aSource->EndTimeL());
    if( err1 != KErrNone )
        {
	TTime time = Time::NullTTime();
	endCalTime.SetTimeUtcL(time);
	}
 
    aTarget->SetStartAndEndTimeL(startCalTime, aSource->EndTimeL());
 
    if( aSource->EntryTypeL() == CCalEntry::ETodo )
        {
	TCalTime time = aSource->CompletedTimeL();
	if( time.TimeUtcL() != Time::NullTTime() ) aTarget->SetCompletedL     
                 (ETrue time);
	aSource->CompletedTimeL().TimeUtcL(), ETrue, 3);
	}
 
    TCalRRule rule;
    if( aSource->GetRRuleL( rule ) != EFalse ) aTarget->SetRRuleL( rule );
 
    RArray<TCalTime> array;
    aSource->GetRDatesL( array );
    if( array.Count() > 0 )	aTarget->SetRDatesL( array );
 
    array.Reset();
    aSource->GetExceptionDatesL( array );
    if( array.Count() > 0 ) aTarget->SetExceptionDatesL( array );
    array.Close();
 
    CCalAlarm* alarm = aSource->AlarmL();
    aTarget->SetAlarmL( alarm );
    if( alarm )
        {
	aSource->AlarmL()->TimeOffset().Int(), 3);
	delete alarm;
	}
 
    //If,
    //Category, Attendee, Organizer, PhoneOwner, Method,
    //SequenceNumber, RecurrenceID, TzRules, LocalUid are not needed
    }

--Egeri 09:51, 8 June 2007 (UTC)

Rate This

 
Bookmark this page: DeliciousDiggFacebookGoogleYahooStumbleUponRedditFurlTechnocratiMagnoliaTwitter  Share this page Share this page Print this Page Print this page Invite a friend Invite a friend
Email Newsletters Press Terms & Conditions Privacy Policy Sitemap Contact Us © 2009 Nokia 
RDF Facets: qdcZdescriptionQSxEa0E20WikiE20javaE20symbianE5fosE20s60E20maemoE20cE2bE2bE20WikiE20HomeE20WikiE20HelpE20OverviewE20GlossaryE20CreateE20PageE20ProposeE20anE20ArticleE20SpotlightE20TopicE20E2dE20WE52TE20WidgetsE20ProgrammingE20E4canguageE20E2dE20SymbianE20CE2bE2bE20E2dE20OpenE20CE2fCE2bE2bE20E2dE20JavaE20E2dE20FlashE20E4citeE20E2dE20PythonE20WebE20TechnologiesE20E2dE20WE52TE20WidgetsE20E2dE20WidSetsE20ToolsE20andE20SE44KE20CodeE20E45E78amplesE20KnowledgeE20BaseE20TechnologyE20AreasE20SoftwareE20PlatformsE20E44evelopmentE20ProcessE20E3fE3fWikiE20ChineseE20E3fE3fE3fWikiE20JapaneseE20PortugueseE2fBrazilianE20E52ussianE20WhatE20linksE20hereE20UploadE20fileE20SpecialE20pagesE20PrintableE20versionE44ownloadE20asE20PE44FE20GoE20ToE20E2eE2eE2eX qdcZidentifierQSxhttpE3aE2fE2fwikiE2eforumE2enokiaE2ecomE2findeE78E2ephpE2fMMPE5ffileX qdcZpublisherQUxhttpE3aE2fE2fswE2enokiaE2ecomE2fidE2fc764fd1cE2d8b06E2d499aE2d9a6aE2d17c3903d5a65E2fforumE5fnokiaE5fcrawlerE5fagentX qdcZtitleQSxMMPE20fileE20E2dE20ForumE20NokiaE20WikiX qdcZtypeQUqfnZE45E78cludedFromGeneralE4cistingsQ qdcZtypeQUqfnTypeZCommunityContentQ qdcZtypeQUqfnTypeZE52esourceQ qdcZtypeQUqfnTypeZWebpageQ qdcZtypeQUqfnTypeZWikiContentQ qdcZtypeQUqmarsZManagedE52esourceQ qdcZtypeQUqwebZInformationE52esourceQ qdcZtypeQUqwebZPageQ qdcZtypeQUqwebZE52esourceQ qdcZtypeQUqrdfsZE52esourceQ qrssZdescriptionQSxEa0E20WikiE20javaE20symbianE5fosE20s60E20maemoE20cE2bE2bE20WikiE20HomeE20WikiE20HelpE20OverviewE20GlossaryE20CreateE20PageE20ProposeE20anE20ArticleE20SpotlightE20TopicE20E2dE20WE52TE20WidgetsE20ProgrammingE20E4canguageE20E2dE20SymbianE20CE2bE2bE20E2dE20OpenE20CE2fCE2bE2bE20E2dE20JavaE20E2dE20FlashE20E4citeE20E2dE20PythonE20WebE20TechnologiesE20E2dE20WE52TE20WidgetsE20E2dE20WidSetsE20ToolsE20andE20SE44KE20CodeE20E45E78amplesE20KnowledgeE20BaseE20TechnologyE20AreasE20SoftwareE20PlatformsE20E44evelopmentE20ProcessE20E3fE3fWikiE20ChineseE20E3fE3fE3fWikiE20JapaneseE20PortugueseE2fBrazilianE20E52ussianE20WhatE20linksE20hereE20UploadE20fileE20SpecialE20pagesE20PrintableE20versionE44ownloadE20asE20PE44FE20GoE20ToE20E2eE2eE2eX qfnZdistributionQUxhttpE3aE2fE2fwikiE2eforumE2enokiaE2ecomE2fX qfnZtypeQUqfnTypeZCommunityContentQ qfnZtypeQUqfnTypeZE52esourceQ qfnZtypeQUqfnTypeZWebpageQ qfnZtypeQUqfnTypeZWikiContentQ qfnZupdatedQDx2008E2d10E2d02X qfnZuserE5ftagQSxfileX qfnZuserE5ftagQSxlibpathX qfnZuserE5ftagQSxmmpX qfnZuserE5ftagQSxresourceX qmarsZdescriptionQSxEa0E20WikiE20javaE20symbianE5fosE20s60E20maemoE20cE2bE2bE20WikiE20HomeE20WikiE20HelpE20OverviewE20GlossaryE20CreateE20PageE20ProposeE20anE20ArticleE20SpotlightE20TopicE20E2dE20WE52TE20WidgetsE20ProgrammingE20E4canguageE20E2dE20SymbianE20CE2bE2bE20E2dE20OpenE20CE2fCE2bE2bE20E2dE20JavaE20E2dE20FlashE20E4citeE20E2dE20PythonE20WebE20TechnologiesE20E2dE20WE52TE20WidgetsE20E2dE20WidSetsE20ToolsE20andE20SE44KE20CodeE20E45E78amplesE20KnowledgeE20BaseE20TechnologyE20AreasE20SoftwareE20PlatformsE20E44evelopmentE20ProcessE20E3fE3fWikiE20ChineseE20E3fE3fE3fWikiE20JapaneseE20PortugueseE2fBrazilianE20E52ussianE20WhatE20linksE20hereE20UploadE20fileE20SpecialE20pagesE20PrintableE20versionE44ownloadE20asE20PE44FE20GoE20ToE20E2eE2eE2eX qmarsZlanguageQUxhttpE3aE2fE2fswE2enokiaE2ecomE2flanguageE2d1E2fenX qrdfZtypeQUqfnZE45E78cludedFromGeneralE4cistingsQ qrdfZtypeQUqfnTypeZCommunityContentQ qrdfZtypeQUqfnTypeZE52esourceQ qrdfZtypeQUqfnTypeZWebpageQ qrdfZtypeQUqfnTypeZWikiContentQ qrdfZtypeQUqmarsZManagedE52esourceQ qrdfZtypeQUqwebZInformationE52esourceQ qrdfZtypeQUqwebZPageQ qrdfZtypeQUqwebZE52esourceQ qrdfZtypeQUqrdfsZE52esourceQ
User Rating: qfnZuserE5FratingQNx1E2E0000X