diff -r 000000000000 -r e686773b3f54 pimprotocols/pbap/server/pbapcontactdbviews.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pimprotocols/pbap/server/pbapcontactdbviews.cpp Tue Feb 02 10:12:17 2010 +0200 @@ -0,0 +1,612 @@ +// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies). +// All rights reserved. +// This component and the accompanying materials are made available +// under the terms of "Eclipse Public License v1.0" +// which accompanies this distribution, and is available +// at the URL "http://www.eclipse.org/legal/epl-v10.html". +// +// Initial Contributors: +// Nokia Corporation - initial contribution. +// +// Contributors: +// +// Description: +// + +#include "pbapcontactdbviews.h" +#include +#include +#include +#include "pbapserver.h" +#include "btaccesshostlog.h" + +_LIT(KNameFieldSeparator, ";"); + + + +/*static*/ CPbapContactDbViews* CPbapContactDbViews::NewL(CContactDatabase& aDatabase) + { + LOG_STATIC_FUNC + CPbapContactDbViews* self=new(ELeave) CPbapContactDbViews(aDatabase); + return self; + } + + +CPbapContactDbViews::CPbapContactDbViews(CContactDatabase& aDatabase) + : iDatabase(aDatabase) + { + LOG_FUNC + } + + +CPbapContactDbViews::~CPbapContactDbViews() + { + LOG_FUNC + CloseAllViews(); + } + + +/** +Close all currently open views and abort current asynchronous search and sort request. +This function should be called when the PBAP session ends (gracefully or otherwise) +to save memory while there is no PBAP session open +*/ +void CPbapContactDbViews::CloseAllViews() + { + LOG_FUNC + + CancelAndCleanup(); + if (iNameView) + { + iNameView->Close(*this); + iNameView=NULL; + } + if (iPhoneticView) + { + iPhoneticView->Close(*this); + iPhoneticView=NULL; + } + iNameViewReady = EFalse; + iPhoneticViewReady = EFalse; + } + +/** +Get contact name string with fields separated by a semi-colon. The fields are read from the name +view when it is ready, otherwise they are read from the database +*/ +HBufC* CPbapContactDbViews::GetContactNameFromIdL(TContactItemId aContactId) const + { + LOG_FUNC + + HBufC* name=NULL; + + // check to see if the view of the underlying database is ready, i.e. HandleContactViewEvent + // has been called back after we created iNameView + if (iNameViewReady) + { + // find the contact id in the name view + TInt index=iNameView->FindL(aContactId); + User::LeaveIfError(index); //leave with KErrNotFound if contact id not found + + // read concatenated name from name view + name=iNameView->AllFieldsLC(index, KNameFieldSeparator); + } + else + { + // create buffer large enough to contain name fields with separators + // NB We can't use the 'separator' functionality of + // TContactTextDefItem because the separator is elided when there is + // no text in the field following, which isn't what we want. + name=HBufC::NewLC(5*KCntMaxTextFieldLength+4); + + CContactItem* item = iDatabase.ReadMinimalContactLC(aContactId); + const CContactItemFieldSet& fieldSet = item->CardFields(); + + AppendField(name, KUidContactFieldFamilyName, fieldSet); + AppendField(name, KUidContactFieldGivenName, fieldSet); + AppendField(name, KUidContactFieldAdditionalName, fieldSet); + AppendField(name, KUidContactFieldPrefixName, fieldSet); + AppendField(name, KUidContactFieldSuffixName, fieldSet, ETrue); // ETrue- last field: don't add ; + + CleanupStack::PopAndDestroy(item); + } + CleanupStack::Pop(name); //ownership passed + return name; + } + +// Append the field aUid from field set aFieldSet to aBuf. +// Also append KNameFieldSeparator to that, unless aLast. +void CPbapContactDbViews::AppendField(HBufC* aBuf, TUid aUid, const CContactItemFieldSet& aFieldSet, TBool aLast) const + { + LOG_FUNC + TPtr ptr(aBuf->Des()); + const TInt index = aFieldSet.Find(aUid); + if ( index != KErrNotFound ) + { + ptr.Append(aFieldSet[index].TextStorage()->Text()); + } + if ( !aLast ) + { + ptr.Append(KNameFieldSeparator); + } + } + +/** +Finds contacts matching search parameters and returns the ids sorted in the requested order +*/ +void CPbapContactDbViews::GetContactIdsMatchingCriteriaL(SymbianPBAP::TOrder aOrder, SymbianPBAP::TSearchAttribute aSearchAttribute, + const TDesC& aSearchValue, CContactIdArray& aResults, MPbapContactDbViewsCallback& aCallback) + { + LOG_FUNC + + // the only supported sort orders are alphabetical and phonetical + if (aOrder != SymbianPBAP::EAlphabetical && aOrder != SymbianPBAP::EPhonetical) + { + __ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicInvalidViewSortOrder)); + // leave if sort order not supported + User::Leave(KErrNotSupported); + } + + // store callback and results array + iCallback = &aCallback; + iResults = &aResults; + + // abort previous request and cleanup + CancelAndCleanup(); + + // store sort and search parameters + iOrder = aOrder; + iSearchAttribute = aSearchAttribute; + iSearchValue = aSearchValue.AllocL(); + + if (!iNameView) + { + // first request so create name view now + CreateNameViewL(); + } + + if (!iPhoneticView && IsPhoneticViewRequired()) + { + // phonetic search or sort required so create phonetic name view + CreatePhoneticViewL(); + } + + iOpState = EPendingSearch; + if (iNameViewReady && (iPhoneticViewReady || !IsPhoneticViewRequired())) + { + // views are ready (or not required depending on sort order) so start search and sort now + TCallBack callback(SearchAndSortCallback,this); + iAsyncCallback = new(ELeave) CAsyncCallBack(callback, CActive::EPriorityStandard); + iAsyncCallback->CallBack(); + } + } + + +void CPbapContactDbViews::CancelSearchAndSortRequest() + { + LOG_FUNC + CancelAndCleanup(); + iCallback=NULL; + } + + +void CPbapContactDbViews::CreateNameViewL() + { + LOG_FUNC + // check name view does not already exist + if(iNameView) + { + __ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicNameViewExists)); + User::Leave(KErrAlreadyExists); + } + + RContactViewSortOrder viewSortOrder; + CleanupClosePushL(viewSortOrder); + viewSortOrder.AppendL(KUidContactFieldFamilyName); + viewSortOrder.AppendL(KUidContactFieldGivenName); + viewSortOrder.AppendL(KUidContactFieldAdditionalName); + viewSortOrder.AppendL(KUidContactFieldPrefixName); + viewSortOrder.AppendL(KUidContactFieldSuffixName); + + const TContactViewPreferences prefs = static_cast(EContactsOnly | EUnSortedAtEnd); + iNameView = CContactLocalView::NewL(*this, iDatabase, viewSortOrder, prefs); + CleanupStack::PopAndDestroy(); // viewSortOrder + } + + +void CPbapContactDbViews::CreatePhoneticViewL() + { + LOG_FUNC + // check phonetic view does not already exist + if(iPhoneticView) + { + __ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicPhoneticViewExists)); + User::Leave(KErrAlreadyExists); + } + + RContactViewSortOrder viewSortOrder; + CleanupClosePushL(viewSortOrder); + viewSortOrder.AppendL(KUidContactFieldFamilyNamePronunciation); + viewSortOrder.AppendL(KUidContactFieldGivenNamePronunciation); + + const TContactViewPreferences prefs = static_cast(EContactsOnly | EUnSortedAtEnd); + iPhoneticView = CContactLocalView::NewL(*this, iDatabase, viewSortOrder, prefs); + CleanupStack::PopAndDestroy(); // viewSortOrder + } + + +TBool CPbapContactDbViews::IsPhoneticViewRequired() const + { + LOG_FUNC + return (iOrder==SymbianPBAP::EPhonetical || iSearchAttribute==SymbianPBAP::ESound); + } + + +/*static*/ TInt CPbapContactDbViews::SearchAndSortCallback(TAny* aAny) + { + LOG_STATIC_FUNC + CPbapContactDbViews* self = static_cast(aAny); + self->DoSearchAndSortCallback(); + return KErrNone; + } + +void CPbapContactDbViews::DoSearchAndSortCallback() + { + LOG_FUNC + if (iOpState == EPendingSearch) + { + iOpState = ESearching; + TRAPD(error, DoSearchAndSortL()); + if (error != KErrNone) + { + LOG1(_L("Error %d from DoSearchAndSortL"), error); + // NotifySearchAndSortComplete will also cancel and cleanup search + NotifySearchAndSortComplete(error); + } + } + } + + +void CPbapContactDbViews::DoSearchAndSortL() + { + LOG_FUNC + if (!iSearchValue->Length()) + { + // no search value so just sort + DoSortL(); + } + else + { + if (iSearchAttribute==SymbianPBAP::ENumber) + { + FindInPhoneFieldsL(); + } + else if (iSearchAttribute==SymbianPBAP::EName) + { + FindInViewL(*iNameView); + } + else if (iSearchAttribute==SymbianPBAP::ESound) + { + FindInViewL(*iPhoneticView); + } + } + } + + +void CPbapContactDbViews::FindInViewL(CContactViewBase& aView) + { + LOG_FUNC + // check search values in correct state + if(iSearchValueArray || iFindView) + { + __ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicInvalidViewSearchState)); + User::Leave(KErrAlreadyExists); + } + + iSearchValueArray = new(ELeave) CPtrCArray(1); + iSearchValueArray->AppendL(*iSearchValue); + + // start asynchronous search of view + iFindView = CContactFindView::NewL(iDatabase, aView, *this, iSearchValueArray, CContactViewBase::EPrefixSearch); + } + + +void CPbapContactDbViews::FindInPhoneFieldsL() + { + LOG_FUNC + // check search values in correct state + if(iFindFieldDef || iIdleFinder) + { + __ASSERT_DEBUG(EFalse, Panic(EPbapServerPanicInvalidFieldSearchState)); + User::Leave(KErrAlreadyExists); + } + + iFindFieldDef = new(ELeave) CContactItemFieldDef; + iFindFieldDef->AppendL(KUidContactFieldPhoneNumber); + + // start an asynchronous search of phone fields in database + iIdleFinder = iDatabase.FindAsyncL(*iSearchValue, iFindFieldDef, this); + } + + +void CPbapContactDbViews::IdleFindCallback() + { + LOG_FUNC + TInt idleFindError = iIdleFinder->Error(); + if(idleFindError == KErrNone) + { + // this callback will be called for every 16 items searched to allow users to update their search + // status but we are only concerned when the entire search has completed + if(iIdleFinder->IsComplete()) + { + // find completed successfully + iOpState = ESorting; + + TRAPD(error, HandleIdleFindCompleteL()); + if (error != KErrNone) + { + LOG1(_L("Error %d from HandleIdleFindCompleteL"), error); + // NotifySearchAndSortComplete will also cancel and cleanup search + NotifySearchAndSortComplete(error); + } + } + } + else + { + LOG1(_L("Idle finder error %d"), idleFindError); + // error occurred during find notify the observer (and abort find) + NotifySearchAndSortComplete(idleFindError); + } + } + + +void CPbapContactDbViews::HandleIdleFindCompleteL() + { + LOG_FUNC + CContactIdArray* contactIds=iIdleFinder->TakeContactIds(); + LOG1(_L("Idle finder returned %d contacts"), contactIds->Count()); + CleanupStack::PushL(contactIds); + DoSortSearchResultsL(*contactIds); + CleanupStack::PopAndDestroy(contactIds); + } + + +void CPbapContactDbViews::HandleFindInViewCompleteL() + { + LOG_FUNC + CContactIdArray* contactIds = GetContactIdsInViewL(*iFindView); + LOG1(_L("Find view contains %d contacts"), contactIds->Count()); + CleanupStack::PushL(contactIds); + DoSortSearchResultsL(*contactIds); + CleanupStack::PopAndDestroy(contactIds); + } + + +void CPbapContactDbViews::DoSortL() + { + LOG_FUNC + // get all the ids from the view + CContactIdArray* contactIds=NULL; + switch (iOrder) + { + case SymbianPBAP::EAlphabetical: + contactIds = GetContactIdsInViewL(*iNameView); + break; + case SymbianPBAP::EPhonetical: + contactIds = GetContactIdsInViewL(*iPhoneticView); + break; + default: + Panic(EPbapServerPanicInvalidViewSortOrder); + } + + if (contactIds) + { + CleanupStack::PushL(contactIds); + CopyToResultsArrayL(*contactIds); + CleanupStack::PopAndDestroy(contactIds); + } + + // the search and sort request is complete notify the observer + NotifySearchAndSortComplete(KErrNone); + } + + +CContactIdArray* CPbapContactDbViews::GetContactIdsInViewL(CContactViewBase &aView) + { + LOG_FUNC + CContactIdArray* contactIds = CContactIdArray::NewLC(); + CArrayFixFlat* indexArray = new(ELeave) CArrayFixFlat(32); + CleanupStack::PushL(indexArray); + TInt count=aView.CountL(); + for (TInt index=0; index < count; ++index) + { + indexArray->AppendL(index); + } + + aView.GetContactIdsL(*indexArray, *contactIds); + CleanupStack::PopAndDestroy(indexArray); + CleanupStack::Pop(contactIds); + return contactIds; //ownership passed + } + + +void CPbapContactDbViews::DoSortSearchResultsL(CContactIdArray& aContactIds) + { + LOG_FUNC + + if (iOrder == SymbianPBAP::EAlphabetical) + { + if (iSearchAttribute != SymbianPBAP::EName) + { + // the find view was sorted on the phonetic name so we need to resort the + // ids in the find view to match the name view ordering + ResortIdsInViewOrderL(aContactIds, *iNameView); + } + } + else if (iOrder == SymbianPBAP::EPhonetical) + { + if (iSearchAttribute != SymbianPBAP::ESound) + { + // the find view was sorted on the name so we need to resort the + // ids in the find view to match the phonetic name view ordering + ResortIdsInViewOrderL(aContactIds, *iPhoneticView); + } + } + + CopyToResultsArrayL(aContactIds); + + // the search and sort is complete notify the observer + NotifySearchAndSortComplete(KErrNone); + } + +/** +aView will be sorted in a specific order and this function re-orders the contact ids in +aContectIds to be in the same order +*/ +void CPbapContactDbViews::ResortIdsInViewOrderL(CContactIdArray& aContactIds, CContactViewBase& aView) + { + LOG_FUNC + // create sorted array of indexes of ids in the view + RArray indexes; + CleanupClosePushL(indexes); + TInt count = aContactIds.Count(); + for (TInt i = 0; i < count; ++i) + { + TInt pos = aView.FindL(aContactIds[i]); + if (pos != KErrNotFound) + { + indexes.InsertInOrderL(pos); + } + } + + // now replace id array contents with ids sorted in same order as view + aContactIds.Reset(); + count = indexes.Count(); + for (TInt j = 0; j < count; ++j) + { + aContactIds.AddL(aView.AtL(indexes[j])); + } + CleanupStack::PopAndDestroy(); //indexes + } + + +void CPbapContactDbViews::CopyToResultsArrayL(const CContactIdArray& aContactIds) + { + LOG_FUNC + const TInt count = aContactIds.Count(); + for (TInt i=0; i < count; ++i) + { + iResults->AddL(aContactIds[i]); + } + } + +/** +this function is part of MContactViewObserver and is called back as a result +of an event occurring on a specific view, in our case iNameView or iPhoneticView +*/ +void CPbapContactDbViews::HandleContactViewEvent(const CContactViewBase &aView, + const TContactViewEvent &aEvent) + { + LOG_FUNC + TInt error=KErrNone; + switch(aEvent.iEventType) + { + case TContactViewEvent::ESortOrderChanged: + case TContactViewEvent::EReady: + { + if (&aView == iNameView) + { + iNameViewReady = ETrue; + } + else if (&aView == iPhoneticView) + { + iPhoneticViewReady = ETrue; + } + else if (&aView == iFindView && iOpState == ESearching) + { + iOpState = ESorting; + TRAP(error, HandleFindInViewCompleteL()); + if (error != KErrNone) + { + LOG1(_L("Error %d from HandleFindInViewCompleteL"), error); + // NotifySearchAndSortComplete will also cancel and cleanup search + NotifySearchAndSortComplete(error); + } + return; + } + + if (iNameViewReady && (iPhoneticViewReady || !IsPhoneticViewRequired()) + && iOpState == EPendingSearch) + { + // necessary views are ready now so start the find and sort + iOpState = ESearching; + TRAP(error, DoSearchAndSortL()); + if (error != KErrNone) + { + LOG1(_L("Error %d from DoSearchAndSort"), error); + // NotifySearchAndSortComplete will also cancel and cleanup search + NotifySearchAndSortComplete(error); + } + } + } + break; + case TContactViewEvent::EUnavailable: + case TContactViewEvent::EServerError: + case TContactViewEvent::ESortError: + { + if (iOpState != EIdle) + { + LOG1(_L("Error %d reported by contact views"), aEvent.iInt); + // error during find and sort request + NotifySearchAndSortComplete(aEvent.iInt); + } + } + break; + default: + break; + } + } + + +void CPbapContactDbViews::NotifySearchAndSortComplete(TInt aError) + { + LOG_FUNC + if (iCallback) + { + iCallback->HandleSearchAndSortComplete(aError); + iCallback = NULL; + } + CancelAndCleanup(); + } + + +void CPbapContactDbViews::CancelAndCleanup() + { + LOG_FUNC + iOpState=EIdle; + + if (iFindView) + { + iFindView->Close(*this); + iFindView=NULL; + } + delete iSearchValue; + iSearchValue=NULL; + delete iSearchValueArray; + iSearchValueArray=NULL; + delete iFindFieldDef; + iFindFieldDef=NULL; + + if (iIdleFinder) + { + iIdleFinder->Cancel(); + delete iIdleFinder; + iIdleFinder=NULL; + } + if (iAsyncCallback) + { + iAsyncCallback->Cancel(); + delete iAsyncCallback; + iAsyncCallback=NULL; + } + }