--- /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 <cntfldst.h>
+#include <cntitem.h>
+#include <cntfield.h>
+#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<TContactViewPreferences>(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<TContactViewPreferences>(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<CPbapContactDbViews*>(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<TInt>* indexArray = new(ELeave) CArrayFixFlat<TInt>(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<TInt> 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;
+ }
+ }