quick_recipes (779892), страница 23
Текст из файла (страница 23)
Remember to use a CContactItemViewDef, asin Recipe 4.2.3.2.The recipe is used to find out which contacts contain some string inone (or any) of their fields. The API we are going to use requires you toimplement a word-parsing method. You can look for contacts using a fullsentence but the internal engine only supports single words. So it is upCONTACTS AND CALENDAR129to you to tell the engine how to extract single words out of a sentence,according to your own set of rules. The code is implemented using aTCallback, but it doesn’t require an observer interface.First open the database as in Recipe 4.2.3.1:#include#include#include#include#include<cntdb.h> // and link to cntmodel.lib<cntitem.h><cntfield.h><cntdef.h><badesca.h> // and link to bafl.lib (for descriptor arrays)class CContactRead : public CBase{public:static CContactRead* NewL();∼CContactRead(); // to delete iCntDbpublic:static TInt ParseWord(TAny*);CContactIdArray* FindWithCallbackLC(TFieldType aField,const TDesC& aMatch);private:void ConstructL();CContactRead();private:TCallBack iCallback;CContactDatabase* iCntDb;};// we need to setup the callback.CContactRead::CContactRead(): iCallback(&CContactRead::ParseWord){}// runs the search and transfer ownership of// the returned object to the caller.CContactIdArray* CContactRead::FindWithCallbackL(TFieldType aField,const TDesC& aMatch){TContactTextDefItem field(aField);CContactTextDef* textDef = CContactTextDef::NewL();CleanupStack::PushL(textDef);textDef->AppendL(field);// we need a descriptor array to setup the searchCDesCArray* match = new (ELeave) CDesCArrayFlat(1);CleanupStack::PushL(match);match->AppendL(aMatch);CContactIdArray* result = iCntDb->FindInTextDefLC(*match,textDef, iCallback);CleanupStack::Pop(result);CleanupStack::PopAndDestroy(2, textDef);return result;}130SYMBIAN C++ RECIPES// The whole purpose of the callback at this point is to take the// content of the aMatch parameter in CContactRead::FindByCallbackL()// and split it into words, presumably using punctuation and spaces.// This is just a sample implementation designed for illustration.// Note that ParseWord can’t leave.TInt CContactRead::ParseWord(TAny* aParam){SFindInTextDefWordParser* parserStruct =(SFindInTextDefWordParser*)aParam;TPtrC searchString(*(parserStruct->iSearchString));TInt index = KErrNotFound;TInt error = KErrNone;while(0 <= (index = searchString.Locate(' '))){if (index > 0){TRAP(error,parserStruct->iWordArray->AppendL(searchString.Left(index)));if (error != KErrNone)return error;if (searchString.Length() > index + 1){searchString.Set(searchString.Mid(index + 1));}else{searchString.Set(KNullDesC());break;}}else{// remove first character as it is a spacesearchString.Set(searchString.Mid(1));}}if(searchString.Length() > 0) // the last word{TRAP(error,parserStruct->iWordArray->AppendL(searchString));if (error != KErrNone)return error;}return KErrNone;}Tip: On large contact databases, a search can end up taking sometime.
This is why there is an asynchronous method you can use:CContactDatabase::FindInTextDefAsyncL(). See the codesample in \PIM\ContactAsynchFind for more details.CONTACTS AND CALENDAR131What may go wrong when you do this: Well, searching itself is notmuch of a problem. Reading all the contacts you found afterwardscan take a huge amount of time if there are too many. Depending onthe circumstances, you may want to discover them in smaller batches(i.e., using CIdle).4.2.5.2Move a Contact to Another GroupAmount of time required: 1 hourLocation of example code: \PIM\ContactMoveRequired library(s): cntmodel.libRequired header file(s): cntdb.h, cntitem.h, cntfield.h,cntdef.hRequired platform security capability(s): ReadUserData WriteUserDataProblem: You want to remove a contact card from one group and thenadd it to another group.Solution: In order to make the problem a little more challenging, we aregoing to assume that the group we want to add the contact to is onlyknown by its name (or label).
That way, we also have to find it first.However, even though contacts can belong to several groups at once,we are just going to remove the contact from the first group we find itbelonging to. Adding a loop to handle multiple groups is trivial.First of all, open the database as you did in Recipe 4.2.3.1:#include#include#include#include<cntdb.h> // and link to cntmodel.lib<cntitem.h><cntfield.h><cntdef.h>class CContactWrite : public CBase{public:static CContactWrite* NewL();∼CContactWrite(); // to delete iCntDbvoid MoveContactL(TContactItemId aCntId,const TDesC& aNewGroup);private:void ConstructL();private:CContactDatabase* iCntDb;};void CContactWrite::MoveContactL(TContactItemId aCntId,const TDesC& aNewGroup){// open the contact item for exclusive useCContactItem* item = iCntDb->OpenContactLX(aCntId);CleanupStack::PushL(item);132SYMBIAN C++ RECIPES// Let’s find out what group the contact already belongs toTContactItemId groupId = KNullContactId;CContactItem* group = NULL;if (KUidContactCard == item->Type()){CContactCard* card = (CContactCard*)item;CContactIdArray* ids = card->GroupsJoinedLC();if (0 < ids->Count()){groupId = (*ids)[0];}CleanupStack::PopAndDestroy(ids);}if (KNullContactId != groupId){// let’s remove the contact from the groupgroup = iCntDb->OpenContactLX(groupId);CleanupStack::PushL(group);iCntDb->RemoveContactFromGroupL(*item, *group);CleanupStack::PopAndDestroy(group);CleanupStack::PopAndDestroy(); // exclusive lock on groupgroup = NULL;}CleanupStack::PopAndDestroy(2); // item, exclusive lock on item// let’s find the new group in the database, or create itgroupId = KNullContactId;CContactIdArray* groupArrayId = iCntDb->GetGroupIdListL();if (NULL != groupArrayId){CleanupStack::PushL(groupArrayId);for (TInt ii = 0 ; ii < groupArrayId->Count() ; ++ii){group = iCntDb->ReadContactLC((*groupArrayId)[ii]);TPtrC label = ((CContactGroup*)group)->GetGroupLabelL();if (aNewGroup.MatchC(label)){groupId = (*groupArrayId)[ii];ii = groupArrayId->Count(); // break}CleanupStack::PopAndDestroy(group);group = NULL;}CleanupStack::PopAndDestroy(groupArrayId);}if (KNullContactId == groupId){group = iCntDb->CreateContactGroupLC(aNewGroup);groupId = group->Id();CleanupStack::PopAndDestroy(group);}// let’s add the contact to the groupiCntDb->AddContactToGroupL(aCntId, groupId);}CONTACTS AND CALENDAR133Tip: Here, we face the issue of non-atomic operations.
If the methodfails halfway through, the contact may become an orphan. Rolling thechanges back requires a considered approach, which should be addedexplicitly because you cannot rely on the cleanup stack to unwindevery transaction.4.2.5.3Find Out If You are AvailableAmount of time required: 45 minutesLocation of example code: \PIM\AgendaSearchRequired library(s): calinterimapi.libRequired header file(s): calsession.h, calinstanceview.h,calinstance.h, calprogresscallback.hRequired platform security capability(s): ReadUserData WriteUserDataProblem: You want to know what is in the calendar at a given time slot.Solution: At this level, we don’t care that much about calendar entriesbut rather about entry instances (an instance exists every time an entry isrepeated).
The API that allows us to filter the database using a time slotis CalCommon::TCalViewFilter, which has several subclasses, fordifferent purposes.#include <calsession.h> // and link to calinterimapi.lib#include <calinstanceview.h>#include <calinstance.h>#include <calprogresscallback.h>#include <mmf\common\mmfcontrollerpluginresolver.h>// this last on is for the ResetAndDestroy TCleanupItemclass CCalRead : public CBase, public MCalProgressCallBack{public:static CCalRead* NewL();∼CCalRead(); // to delete iCalSes and iCalViewTBool CheckFreeL(TTime aStartUtc, TTimeIntervalMinutes aSlot);public: // from MCalProgressCallBackvoid Progress(TInt){};void Completed(TInt){};TBool NotifyProgress(){return EFalse;};private:void ConstructL();private:CCalSession* iCalSes;CCalInstanceView* iCalView;};// You need an opened calendar database, which is typically// the default database in the phone.134SYMBIAN C++ RECIPES// You should assume that it may not have been created before.void CCalRead::ConstructL(){iCalSes = CCalSession::NewL();const TDesC& name = iCalSes->DefaultFileNameL();TRAPD(error, iCalSes->CreateCalFileL(name));if (error != KErrAlreadyExists){User::LeaveIfError(error);}iCalSes->OpenL(KNullDesC);iCalView = CCalInstanceView::NewL(*iCalSes, *this);}TBool CCalRead::CheckFreeL(TTime aStartUtc,TTimeIntervalMinutes aSlot){TBool result = ETrue;TCalTime start;TCalTime end;start.SetTimeUtcL(aStartUtc);TTime endUtc = aStartUtc + aSlot;end.SetTimeUtcL(endUtc);CalCommon::TCalTimeRange range(start, end);RPointerArray<CCalInstance> array;CleanupResetAndDestroyPushL(array);iCalView->FindInstanceL(array, CalCommon::EIncludeAll, range);result = (0 == array.Count());CleanupStack::PopAndDestroy(&array);return result;}4.2.5.4Get Attendee ListAmount of time required: 45 minutesLocation of example code: \PIM\AgendaPeopleRequired library(s): calinterimapi.libRequired header file(s): calsession.h, calentryview.h,calprogresscallback.h, caluser.hRequired platform security capability(s): ReadUserData WriteUserDataProblem: You want to find the addresses of the attendees who acceptedan invitation.Solution: What you want is a descriptor array that will contain the emailaddresses of everybody who is going to attend the meeting.
Once youhave selected the meeting you are interested in, you need to go throughits list of attendees and read the stored address of each one.#include <calsession.h> // and link to calinterimapi.lib#include <calentryview.h>CONTACTS AND CALENDAR#include <calprogresscallback.h>#include <caluser.h>#include <badesca.h> // and link to bafl.libclass CCalRead : public CBase, public MCalProgressCallBack{public:static CCalRead* NewL();∼CCalWrite(); // to delete iCalSes and iCalViewCDesCArray* GetAttendeesL(TCalLocalUid aEventId);public: // from MCalProgressCallBackvoid Progress(TInt){};void Completed(TInt){};TBool NotifyProgress(){return EFalse;};private:void ConstructL();private:CCalSession* iCalSes;CCalEntryView* iCalView;};// You need an opened calendar database, which is typically// the default database in the phone.// You should assume that it may not have been created before.void CCalRead::ConstructL(){iCalSes = CCalSession::NewL();const TDesC& name = iCalSes->DefaultFileNameL();TRAPD(error, iCalSes->CreateCalFileL(name));if (error != KErrAlreadyExists){User::LeaveIfError(error);}iCalSes->OpenL(KNullDesC);iCalView = CCalEntryView::NewL(*iCalSes, *this);}// Transfers ownership of result to callerCDesCArray* CCalRead::GetAttendeesL(TCalLocalUid aEventId){CDesCArray* result = NULL;// find the eventCCalEntry* entry = iCalView->FetchL(aEventId);// get attendeesif (NULL != entry){CleanupStack::PushL(entry);RPointerArray<CCalAttendee>& list = entry->AttendeesL();if (list.Count() > 0){result = new (ELeave) CDesCArrayFlat(list.Count());CleanupStack::PushL(result);for (TInt ii = 0 ; ii < list.Count() ; ++ii){result->AppendL(list[ii]->Address());}CleanupStack::Pop(result);}135136SYMBIAN C++ RECIPESCleanupStack::PopAndDestroy(entry);}return result;}What may go wrong when you do this: CCalAttendee is basicallya placeholder for contact information and the format of the datait contains is not as strictly enforced as it could be.