pimprotocols/pbap/server/pbapcontactdbviews.cpp
changeset 0 e686773b3f54
--- /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;
+		}
+	}