phonebookengines/contactsmodel/cntview/LocalView.cpp
changeset 0 e686773b3f54
child 23 5586b4d2ec3e
child 24 0ba2181d7c28
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/phonebookengines/contactsmodel/cntview/LocalView.cpp	Tue Feb 02 10:12:17 2010 +0200
@@ -0,0 +1,1752 @@
+// Copyright (c) 2001-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 <phbksync.h>
+#include "CNTSTD.H"
+#include <cntviewbase.h>
+#include <cntitem.h>
+#include "cntviewprivate.h"
+#include <cntviewsortplugin.h>
+#include "persistencelayer.h"
+#include "cviewiterator.h"
+#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS
+#include <cntviewsortpluginbase.h>
+#endif
+//uncomment to test
+//#define __PROFILE_SORT__
+
+//uncomment for commonly required debug printing
+//#define __VERBOSE_DEBUG__
+
+extern void DebugLogNotification(const TDesC& aMethod, const TContactDbObserverEvent &aEvent);
+
+//
+// CContactLocalView.
+//
+
+// CIdle Callback function's return values: 0 - finished, 1 - call again
+// (CIdle is used to Insert & Sort contacts one at a time into the view)
+
+const TInt	KSortFinished = 0;
+const TInt	KSortCallAgain = 1;
+
+// Tunable constants
+// These seem to be useful values for memory allocations with RPointerArray
+const TInt	KContactsArrayGranularity = 100;
+const TInt	KUnsortedArrayGranularity = 16;
+// Number of Contacts to process per invocation of the Sorter
+// Local Views can't do much whilst sorting, but we want to allow other
+// Active Objects in the thread to run.
+const TInt	KNumberOfContactsPerChunk = 50;
+
+CContactLocalView::CContactLocalView(const CContactDatabase& aDb,TContactViewPreferences aContactTypes, MLplPersistenceLayerFactory* aFactory) 
+: CContactViewBase(aDb), 
+iFactory(aFactory),
+iContacts(KContactsArrayGranularity), 
+iUnSortedContacts(KUnsortedArrayGranularity),
+iViewPreferences(aContactTypes)
+/**
+@internalComponent
+*/
+	{
+	}
+
+EXPORT_C CContactLocalView::CContactLocalView(const CContactDatabase& aDb,TContactViewPreferences aContactTypes) 
+: CContactViewBase(aDb), iContacts(KContactsArrayGranularity), iUnSortedContacts(KUnsortedArrayGranularity),
+iViewPreferences(aContactTypes)
+/** Protected C++ constructor.
+
+Called by NewL().
+
+@param aDb The underlying database that contains the contact items.
+@param aContactTypes Specifies which types of contact items should be included 
+in the view and the behaviour for items that do not have content in any of 
+the fields specified in the sort order. */
+	{
+	}
+
+EXPORT_C CContactLocalView::~CContactLocalView()
+/** Destructor.
+
+Deletes all resources owned by the object, and removes itself as the contact 
+database observer. */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewDestructor);
+#endif
+	delete iTextDef;
+	delete iAsyncSorter;
+	iContacts.ResetAndDestroy();
+	iUnSortedContacts.ResetAndDestroy();
+	iOutstandingEvents.Close();
+	iSortOrder.Close();
+
+	if (&iDb != NULL)
+		{
+        const_cast<CContactDatabase&>(iDb).RemoveObserver(*this);
+		}
+	delete iViewIterator;
+	}
+
+EXPORT_C CContactLocalView* CContactLocalView::NewL(MContactViewObserver& aObserver,const CContactDatabase& aDb,
+													const RContactViewSortOrder& aSortOrder,TContactViewPreferences aContactTypes)
+/** Allocates and constructs the local view object.
+
+The view is sorted according to the sort order and view preferences specified, 
+using a low priority idle time active object. The specified view observer 
+is notified when the view is sorted and ready for use.
+
+@param aObserver An observer that receives notifications when this view is 
+ready for use and when changes take place in it. The observer receives a TContactViewEvent::EReady 
+event when the view is ready. Any attempt to use the view before this notification will Leave with KErrNotReady
+@param aDb The underlying database that contains the contact items. The view 
+observes the database, so that it handles change events sent from the database.
+@param aSortOrder Specifies the fields to use to sort the items in the view.
+@param aContactTypes Specifies which types of contact items should be included 
+in the view and the behaviour for items that do not have content in any of 
+the fields specified in the sort order.
+@return The newly constructed local view object. */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiNewL, aSortOrder, aContactTypes);
+#endif
+	CContactLocalView* self=new(ELeave) CContactLocalView(aDb,aContactTypes);
+	CleanupStack::PushL(self);
+	self->ConstructL(aObserver,aSortOrder);
+	CleanupStack::Pop(self); 
+	return self;
+	}
+
+EXPORT_C void CContactLocalView::ConstructL(MContactViewObserver& aObserver,const RContactViewSortOrder& aSortOrder)
+/** Protected second phase constructor.
+
+The view is sorted according to the sort order and view preferences specified, 
+using a low priority idle time active object. The specified view observer 
+is notified when the view is sorted and ready for use.
+
+Called by NewL().
+
+@param aObserver An observer that receives notifications when this view is 
+ready for use and when changes take place in it. The observer receives a TContactViewEvent::EReady 
+event when the view is ready. Any attempt to use the view before this notification will Leave with KErrNotReady.
+@param aSortOrder Specifies the fields to use to sort the items in the view. */
+	{
+	// call new ConstructL
+	ConstructL(aObserver, aSortOrder, EFalse, KNullDesC8);
+	}
+
+EXPORT_C CContactLocalView* CContactLocalView::NewL(MContactViewObserver& aObserver,const CContactDatabase& aDb,
+													const RContactViewSortOrder& aSortOrder,TContactViewPreferences aContactTypes,
+													const TDesC8& aSortPluginName)
+/** Allocates and constructs the local view object.
+
+The view is sorted according to the sort order and view preferences specified, 
+using a low priority idle time active object. The specified view observer 
+is notified when the view is sorted and ready for use.
+
+@param aObserver An observer that receives notifications when this view is 
+ready for use and when changes take place in it. The observer receives a TContactViewEvent::EReady 
+event when the view is ready. Any attempt to use the view before this notification will Leave with KErrNotReady
+@param aDb The underlying database that contains the contact items. The view 
+observes the database, so that it handles change events sent from the database.
+@param aSortOrder Specifies the fields to use to sort the items in the view.
+@param aContactTypes Specifies which types of contact items should be included 
+in the view and the behaviour for items that do not have content in any of 
+the fields specified in the sort order.
+@param aSortPluginName Specifies a plug-in that will be used to compare view contacts
+when the the view is sorted. This name is used by ECOM to select the plugin, and is matched
+with the "default_data" of all ECOM plugins that support the required interface.
+@return The newly constructed local view object. */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiNewL, aSortOrder, aContactTypes, aSortPluginName);
+#endif
+	CContactLocalView* self=new(ELeave) CContactLocalView(aDb,aContactTypes);
+	CleanupStack::PushL(self);
+	self->ConstructL(aObserver, aSortOrder, ETrue, aSortPluginName);
+	CleanupStack::Pop(self); 
+	return self;
+	}
+
+/** CContactLocalView contructor, used in the server
+@internalTechnology 
+ */
+EXPORT_C CContactLocalView* CContactLocalView::NewL(MContactViewObserver& aObserver,const CContactDatabase& aDb,const RContactViewSortOrder& aSortOrder,TContactViewPreferences aContactTypes,
+		MLplPersistenceLayerFactory* aFactory,const TDesC8& aSortPluginName)
+	{
+	CContactLocalView* self=new(ELeave) CContactLocalView(aDb,aContactTypes,aFactory);
+	CleanupStack::PushL(self);
+	self->ConstructL(aObserver, aSortOrder, ETrue, aSortPluginName);
+	CleanupStack::Pop(self); 
+	return self;
+	}
+
+void CContactLocalView::ConstructL(MContactViewObserver& aObserver,const RContactViewSortOrder& aSortOrder,
+								   TBool aUseNamedPlugin, const TDesC8& aSortPluginName)
+/** Protected second phase constructor.
+
+The view is sorted according to the sort order and view preferences specified, 
+using a low priority idle time active object. The specified view observer 
+is notified when the view is sorted and ready for use.
+
+Called by NewL().
+
+@internalComponent
+@param aObserver An observer that receives notifications when this view is 
+ready for use and when changes take place in it. The observer receives a TContactViewEvent::EReady 
+event when the view is ready. Any attempt to use the view before this notification will Leave with KErrNotReady.
+@param aSortOrder Specifies the fields to use to sort the items in the view. 
+@param aUseNamedPlugin A flag indicates whether the aSortPluginName parameter is valid.
+@param aSortPluginName Specifies a plug-in that will be used to compare view contacts
+when the the view is sorted. This name is used by ECOM to select the plugin, and is matched
+with the "default_data" of all ECOM plugins that support the required interface.
+*/
+	{
+	CContactViewBase::ConstructL();
+	if(iFactory == NULL)
+		{
+		iFactory = const_cast<CContactDatabase&>(iDb).FactoryL();
+		}
+	iAsyncSorter = CIdleContactSorter::NewL(*this, *iFactory);
+
+	OpenL(aObserver);
+	if (aUseNamedPlugin)
+		{
+		// find and load Sort plug-in
+		if (aSortPluginName.Length())
+			{
+			TUid sortPluginUid = FindSortPluginImplL (aSortPluginName);
+			LoadViewSortPluginL(sortPluginUid, iViewPreferences);
+			}
+		}
+	else
+		{
+		// find and load default Sort plug-in (if any)
+		TUid sortPluginUid = FindDefaultViewSortPluginImplL();
+		if (sortPluginUid != KNullUid)
+			{
+			LoadViewSortPluginL(sortPluginUid, iViewPreferences);
+			}
+		}
+	// initialise for sort, and start if the database is ready
+	InitialiseSortL(aSortOrder, EFalse);
+	if (&iDb != NULL)
+		{
+        const_cast<CContactDatabase&>(iDb).AddObserverL(*this);
+		}
+	}
+
+
+EXPORT_C const RContactViewSortOrder& CContactLocalView::SortOrder() const
+/** Gets the sort order, as set during construction.
+
+@return The sort order. */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiSortOrder);
+#endif
+	return iSortOrder;
+	}
+
+TContactItemId CContactLocalView::AtL(TInt aIndex) const
+/** Returns the ID of the contact item at a specified index into the view.
+
+@param aIndex An index into the view.
+@leave KErrNotFound The index is out of bounds.
+@return The ID of the contact item at the specified index.
+@leave KErrNotReady The view is not ready for use.  */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiAtL, aIndex);
+#endif
+	const CViewContact& contact = ContactAtL(aIndex);
+	return contact.Id();
+	}
+
+const CViewContact& CContactLocalView::ContactAtL(TInt aIndex) const
+/** Returns the contact item at a specified index into the view.
+
+@param aIndex An index into the view.
+@leave KErrNotFound The index is out of bounds.
+@leave KErrNotReady The view is not ready for use.
+@return The contact item at the specified index. */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiContactAtL, aIndex);
+#endif
+	// state cannot be EInitializing or ENotReady
+	if( iState != EReady )
+		{
+		User::Leave(KErrNotReady);
+		}
+
+	TInt offsetIndex=aIndex;
+	const TInt unsortedCount=iUnSortedContacts.Count();
+	const TInt sortedCount=iContacts.Count();
+	if(offsetIndex >= (unsortedCount+sortedCount))
+		{
+		//Out of Bounds.
+		User::Leave(KErrNotFound);
+		}
+
+	if(unsortedCount>0)
+		{
+		if(iViewPreferences & EUnSortedAtBeginning)
+			{
+			if(aIndex<unsortedCount)
+				{
+				//contact in unsorted array
+				return *iUnSortedContacts[aIndex];
+				}
+			else
+				{
+				//contact in sorted array
+				offsetIndex-=unsortedCount;
+				}
+			}
+		else if ((iViewPreferences & EUnSortedAtEnd) && (aIndex>=sortedCount))
+			{
+			offsetIndex-=sortedCount;
+			return *iUnSortedContacts[offsetIndex];
+			}
+
+		}
+	return *iContacts[offsetIndex];
+	}
+
+TInt CContactLocalView::CountL() const
+/** Gets the total number of contact items in the view.
+
+@return The number of contact items in the view. This includes both sorted 
+and unsorted items.
+@leave KErrNotReady The view is not ready for use. */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiCountL);
+#endif
+	// state cannot be EInitializing or ENotReady
+	if( iState != EReady )
+		{
+		User::Leave(KErrNotReady);
+		}
+	
+	TInt count(iUnSortedContacts.Count());
+	count+=iContacts.Count();
+	return count;
+	}
+
+TInt CContactLocalView::FindL(TContactItemId aId) const
+/** Searches for a contact item in the view with the specified ID.
+
+@param aId The ID of the contact item to search for.
+@return If found, the index into the view of the matching item. Otherwise, 
+KErrNotFound.
+@leave KErrNotReady The view is not ready for use.  */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiFindL, aId);
+#endif
+	// state cannot be EInitializing or ENotReady
+	if( iState != EReady )
+		{
+		User::Leave(KErrNotReady);
+		}
+
+	TInt index=KErrNotFound;
+	CViewContact* contact = CViewContact::NewLC(aId);
+	const TInt unSortedCount=iUnSortedContacts.Count();
+	// first look in unsorted contacts
+	if(unSortedCount > 0)
+		{
+		// contact may be in the unsorted array
+		index = iUnSortedContacts.Find(contact,TIdentityRelation<CViewContact>(IdsMatch));
+
+		if ((index != KErrNotFound) && (iViewPreferences & EUnSortedAtEnd))
+			{
+			// account for sorted array size
+			index = index + iContacts.Count();
+			}
+		}
+
+	// if not found try sorted contacts
+	if (index == KErrNotFound)
+		{
+		//contact may be in the sorted array
+		index = iContacts.Find(contact,TIdentityRelation<CViewContact>(IdsMatch));
+
+		if ((index != KErrNotFound) && (iViewPreferences & EUnSortedAtBeginning))
+			{
+			// account for unsorted array size
+			index = index + unSortedCount;
+			}
+		}
+
+	CleanupStack::PopAndDestroy(contact);
+	return index;
+	}
+
+HBufC* CContactLocalView::AllFieldsLC(TInt aIndex,const TDesC& aSeparator) const
+/** Gets a descriptor containing the contents of all fields specified in the view's 
+sort order for an item in the view.
+
+The field separator is used to separate the contents of each field. It is 
+not appended to the last field.
+
+@param aIndex The index of the contact item into the view.
+@param aSeparator The string to use to separate the fields.
+@return Pointer to the contact item descriptor. */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiAllFieldsLC, aIndex);
+#endif
+
+	if( iState != EReady )
+		{
+		User::Leave(KErrNotReady);
+		}
+
+	TInt offsetIndex=aIndex;
+	const TInt unSortedCount=iUnSortedContacts.Count();
+	if(unSortedCount>0)
+		{
+		if(iViewPreferences & EUnSortedAtBeginning)
+			{
+			if(aIndex<unSortedCount)
+				{
+				//contact in unsorted array
+				return FieldsWithSeparatorLC(iUnSortedContacts,aIndex,aSeparator);
+				}
+			else
+				{
+				//contact in sorted array
+				offsetIndex-=unSortedCount;
+				}
+			}
+		else if(iViewPreferences & EUnSortedAtEnd)
+			{
+			const TInt sortedCount=iContacts.Count();
+			if(aIndex>=sortedCount)
+				{
+				offsetIndex-=sortedCount;
+				return FieldsWithSeparatorLC(iUnSortedContacts,offsetIndex,aSeparator);
+				}
+			}
+		}
+	return FieldsWithSeparatorLC(iContacts,offsetIndex,aSeparator);
+	}
+
+EXPORT_C void CContactLocalView::SortL(const RContactViewSortOrder& aSortOrder)
+/** Sorts the view using the specified sort order, using a low priority idle time 
+active object.
+
+This function is called during view construction and on receipt of certain 
+change events from the underlying database.
+
+@param aSortOrder Specifies the fields to use to sort the items in the view. */
+	{
+#ifdef CONTACTS_API_PROFILING
+	TContactsApiProfile::CntViewMethodLog(TContactsApiProfile::ECntVwClassLocalView, TContactsApiProfile::ECntViewApiSortL);
+#endif
+	// re-initialise sort, and try to start it
+	InitialiseSortL(aSortOrder, ETrue);
+	}
+
+
+/*
+ Start first sort of view, or restart after SortL() API has changed the order.
+ */
+void CContactLocalView::InitialiseSortL(const RContactViewSortOrder& aSortOrder, TBool aChangingSortOrder)
+	{
+	if (aChangingSortOrder)
+		{
+		if (&iDb != NULL)
+			{
+			if (!iDb.DatabaseReadyL())
+				{
+				User::Leave(KErrNotReady);
+				}
+			}
+		}
+
+	// copy new sort order
+	TRAPD(sortStartError, iSortOrder.CopyL(aSortOrder));
+	
+	if (sortStartError)
+		{
+		// ensure Db Recover (close then open tables) cannot push view to EReady
+		iExtension->iError = sortStartError;
+		User::Leave(sortStartError);
+		}
+
+	// New sort order for Sort Plugin
+	CViewContactSortPlugin* sortPluginImpl = SortPluginImpl();
+	if (sortPluginImpl)
+		{
+		sortPluginImpl->SetSortOrderL(aSortOrder);
+		}
+
+	// View can Sort only if database is 'ready'.
+	if (&iDb != NULL)
+		{
+		if (!iDb.DatabaseReadyL())
+			{
+			return;
+			}
+		}
+
+	// database is ready for reading - so start the Sort
+	SortL();
+	}
+
+
+/**
+Safe resort of view after Recover, Backup/Restore, etc...
+
+@internalComponent
+@released
+*/
+void CContactLocalView::SafeResort()
+	{
+	TInt sortError(KErrNone);
+	
+	// Database tables are closed across backup or restore so we may need to
+	// re-open view iterator here.
+	if (iViewIterator == NULL)
+		{
+		const TContactViewPreferences viewPrefs = iAsyncSorter->SortViewPreferences();
+		TRAP(sortError,
+			if(iFactory == NULL)
+				{
+				iFactory = const_cast<CContactDatabase&>(iDb).FactoryL();
+				}
+			MLplViewIteratorManager& manager = iFactory->GetViewIteratorManagerL();
+			iViewIterator = new (ELeave) CViewIterator(manager,*iTextDef,viewPrefs);
+			) // TRAP
+		}
+
+	if (!sortError)
+		{
+		TRAP(sortError, SortL());
+		}
+
+	// notify any error
+	if (sortError)
+		{
+		NotifySortError(sortError);
+		}
+	}
+
+
+/**
+@internalComponent
+@released
+*/
+void CContactLocalView::SortL()
+	{
+	// Initialisation for each explicitly requested sort
+	// Construct a text def to read out the required fields from the db.
+	CContactTextDef* textDef=CContactTextDef::NewLC();
+	TInt sortOrderCount=iSortOrder.Count();
+
+	for (TInt sortIndex=0;sortIndex<sortOrderCount;sortIndex++)
+		{
+		textDef->AppendL(TContactTextDefItem(iSortOrder[sortIndex]));
+		}
+	CleanupStack::Pop(); // textDef.
+	delete iTextDef;
+	iTextDef=textDef;
+
+	// NB ResetSortL() requires iTextDef to be initialised
+
+	// initialisation for each pass (of the insert sort) through the db
+	// (2 passes may be required if the SIM card starts locked and is then unlocked)
+	// (such a 2nd pass is kicked off by CIdleContactSorter)
+	ResetSortL();
+
+	// Delete existing sort if present.
+	iAsyncSorter->Stop();
+
+	iContacts.ResetAndDestroy();
+	iUnSortedContacts.ResetAndDestroy();
+
+#ifdef __PROFILE_SORT__
+	RDebug::Print(_L("[CNTMODEL] CntModel View, , %u, %u, Starting sort\n"), 
+		static_cast<TUint>(RProcess().Id()), 
+		static_cast<TUint>(RThread().Id()));
+
+	// 3 timers: 1st for read/Append; 2nd for Sort, 3rd for Compare
+	RDebug::ProfileReset(1,3);
+	RDebug::ProfileStart(1);
+#endif
+
+	// reset sort error
+	iExtension->iError = KErrNone;
+
+	// Kick off idler.
+	iAsyncSorter->Start();
+	}
+
+void CContactLocalView::ResetSortL()
+/**
+ * Setup for a fresh pass through the Contacts database table
+ *
+ * (Code was in SortL)
+ */
+	{
+	delete iViewIterator;
+	iViewIterator = NULL;
+	}
+
+EXPORT_C TInt CContactLocalView::InsertL(TContactItemId aId)
+/** Inserts a contact item into the view, maintaining the view's sort order.
+
+For the item to be inserted, it must exist in the underlying database, and 
+it must be of the correct type according to the view preferences.
+
+This function is called when a contact item or group is added to or changed 
+in the underlying database.
+
+@param aId The ID of a contact item that exists in the underlying database.
+@return The index at which the item was inserted into the view, or KErrNotFound 
+if the contact item was not found in the underlying database, or it already 
+exists in the view. */
+	{
+	TInt index=KErrNotFound;
+#if defined(__VERBOSE_DEBUG__)
+	RDebug::Print(_L("[CNTMODEL] CContactLocalView{ViewPrefs = 0x%08X}::InsertL into view Contact Id %i\r\n"), 
+		iViewPreferences, aId);
+#endif
+	TContactViewPreferences view = iViewPreferences;
+	if(!iAsyncSorter->InsertViewPreferences(view))
+		{
+		return KErrNotFound;
+		}
+	if(iFactory == NULL)
+		{
+		iFactory = const_cast<CContactDatabase&>(iDb).FactoryL();
+		}
+	MLplViewIteratorManager& manager = iFactory->GetViewIteratorManagerL();
+	CViewIterator* iter = new (ELeave) CViewIterator(manager,*iTextDef,view);
+	CleanupStack::PushL(iter);
+	CViewContact* contact = iter->ItemAtL(aId);
+	CleanupStack::PopAndDestroy(iter);	
+	if(contact != NULL && ContactCorrectType(contact->ContactTypeUid(),view))
+		{
+		CleanupStack::PushL(contact);
+		if(IsContactSortable(*contact, iViewPreferences))
+			{
+			//Contact has normal fields and can be added to the standard sorted array				
+#if defined(__VERBOSE_DEBUG__)
+				RDebug::Print(_L("[CNTMODEL] > > > > > View Insert into RPointerArray [Count = %i]\r\n"), iContacts.Count());
+#endif
+				
+			// Insert using Sort Plugin compare method, and get new index
+			User::LeaveIfError(InsertContactInView(iContacts, contact, EFalse, &index));
+			CleanupStack::Pop(contact);
+			if (iViewPreferences & EUnSortedAtBeginning)
+				{
+				index += iUnSortedContacts.Count();
+				}
+			}
+		else if (iViewPreferences & (EUnSortedAtBeginning | EUnSortedAtEnd))
+			{
+			// unsortable contacts go at the end or beginning
+			// we want this to be stable (e.g. when ICC becomes unlocked)
+			User::LeaveIfError(InsertContactInView(iUnSortedContacts, contact, ETrue, &index));
+			CleanupStack::Pop(contact);
+			// calc new index
+			if (iViewPreferences & EUnSortedAtEnd)
+				{
+				index += iContacts.Count();
+				}
+			}
+		else // EIgnoreUnSorted
+			{
+			CleanupStack::PopAndDestroy(contact);
+			}
+		}
+	else if(contact)
+		{
+		delete contact;
+		}
+
+	return index;
+	}
+
+EXPORT_C TInt CContactLocalView::RemoveL(TContactItemId aId)
+/** Removes a contact item from the view.
+
+This function is called when a contact item or group is deleted from or changed 
+in the underlying database.
+
+@param aId The ID of the contact item to remove from the view.
+@return The index of the removed item into the view's list of sorted or unsorted 
+contact items, or KErrNotFound if the item was not found in the view. */
+	{
+	CViewContact* contact = CViewContact::NewLC(aId);
+	TInt index=KErrNotFound;
+	index=iContacts.Find(contact,TIdentityRelation<CViewContact>(IdsMatch));
+	if (index!=KErrNotFound)
+		{
+		CViewContact* temp= iContacts[index];
+		iContacts.Remove(index);
+		delete temp;
+		if (iViewPreferences & EUnSortedAtBeginning)
+			{
+			index+=iUnSortedContacts.Count();
+			}
+		}
+	else
+		{
+		if(iUnSortedContacts.Count()>0)
+			{
+			index=iUnSortedContacts.Find(contact,TIdentityRelation<CViewContact>(IdsMatch));
+			if (index!=KErrNotFound)
+				{
+				CViewContact* temp= iUnSortedContacts[index];
+				iUnSortedContacts.Remove(index);
+				delete temp;
+				if (iViewPreferences & EUnSortedAtEnd)
+					{	
+					index+=iContacts.Count();
+					}
+				// NB - If EIgnoreUnsorted, then this clause would not be running,
+				// as the contact would never be added to the view.
+				}
+			}
+		}
+	CleanupStack::PopAndDestroy(contact);
+	return index;
+	}
+
+EXPORT_C void CContactLocalView::CContactLocalView_Reserved_1()
+	{
+	}
+
+EXPORT_C void CContactLocalView::CContactLocalView_Reserved_2()
+	{
+	}
+
+void CContactLocalView::HandleDatabaseEventL(TContactDbObserverEvent aEvent)
+	{
+	// handle Backup / Restore notifications before checking View State
+	switch (aEvent.iType)
+		{
+		case EContactDbObserverEventBackupBeginning:
+		case EContactDbObserverEventRestoreBeginning:
+#if defined(__VERBOSE_DEBUG__)
+			RDebug::Print(_L("[CNTMODEL] CContactLocalView{ViewPrefs = 0x%08X}::HandleDatabaseEventL -> Backup/Restore Beginning, state = %i\r\n"), 
+				iViewPreferences, iState);
+#endif
+			if (iState == EReady)
+				{
+				SetState(ENotReady);
+				}
+			else
+				{
+				// stop sorting
+				iAsyncSorter->Stop();
+				}
+			ResetSortL();
+			return;
+
+		case EContactDbObserverEventBackupRestoreCompleted:
+#if defined(__VERBOSE_DEBUG__)
+			RDebug::Print(_L("[CNTMODEL] CContactLocalView{ViewPrefs = 0x%08X}::HandleDatabaseEventL -> Backup/Restore Completed, state = %i, old sort error %i\r\n"), 
+				iViewPreferences, iState, iExtension->iError);
+#endif
+			if (iState == ENotReady && iExtension->iError == KErrNone)
+				{
+				// view was ready before tables were closed
+				SetState(EReady);
+				}
+			else // view was Initializing (sorting) before tables were closed
+				{
+				// re-read database and sort
+				SafeResort();
+				}
+			return;
+
+		default:
+			// other events dealt with below
+			break;
+		}
+
+
+	if (iState!=EReady)
+		{
+        // The tables have been closed so the the sort must be cancelled.
+		if (aEvent.iType == EContactDbObserverEventTablesClosed)
+			{
+	    	iAsyncSorter->Stop();
+			}
+
+		if (iAsyncSorter->QueueViewEvents())
+			{
+       			
+#if defined(__VERBOSE_DEBUG__)
+			DebugLogNotification(_L("[CNTMODEL] . . . . . Queueing Database Event "), aEvent);
+#endif
+			iOutstandingEvents.AppendL(aEvent);
+			// The view state is set to ENotReady when a recovery takes place, and also when the tables
+			// are closed, so set ready here.
+			if (iState==ENotReady && (aEvent.iType==EContactDbObserverEventRecover || aEvent.iType==EContactDbObserverEventTablesOpened))
+				{
+				SetState(EReady);
+				}
+			// view was Initializing (sorting) before recovery or compression started!	
+			if (iState==EInitializing && (aEvent.iType==EContactDbObserverEventRecover || aEvent.iType==EContactDbObserverEventCompress))
+				{
+				// re-read database and sort
+				SafeResort();
+				}
+			}		
+			
+			
+#if defined(__VERBOSE_DEBUG__)
+		else
+			{
+			DebugLogNotification(_L("[CNTMODEL] . . . . . Discarding Database Event "), aEvent);
+			}
+#endif
+		}
+	else
+		{
+		TContactViewEvent event;
+		event.iInt = KErrNone;
+		switch(aEvent.iType)
+			{
+			case EContactDbObserverEventGroupChanged:
+				{
+				//Groups are a special case the base view may not contain the group
+				//but a sub view may be such a group and need to know its changed
+				//Local views can contain groups so this case carries on to the next so no break;
+				event.iEventType=TContactViewEvent::EGroupChanged;
+				event.iContactId=aEvent.iContactId;
+				NotifyObservers(event);
+				}
+			case EContactDbObserverEventContactChanged:
+			case EContactDbObserverEventOwnCardChanged:
+				{// Remove from old position, and notify.
+				TRAPD(err,event.iInt=RemoveL(aEvent.iContactId));
+
+                if (err == KErrNone && event.iInt != KErrNotFound)
+                    {
+				    event.iEventType=TContactViewEvent::EItemRemoved;
+				    event.iContactId=aEvent.iContactId;
+				    NotifyObservers(event);
+					}
+				
+				// Insert at new position, and notify.
+				event.iInt=InsertL(aEvent.iContactId);
+                if (event.iInt != KErrNotFound)
+					{
+				    event.iEventType=TContactViewEvent::EItemAdded;
+				    event.iContactId=aEvent.iContactId;
+				    NotifyObservers(event);
+                    }
+				break;
+				}
+			case EContactDbObserverEventContactAdded:
+			case EContactDbObserverEventGroupAdded:
+#if defined(__VERBOSE_DEBUG__)
+			DebugLogNotification(_L("[CNTMODEL] DatabaseEvent -> Contact/Group Added"), aEvent);
+#endif
+				event.iInt=InsertL(aEvent.iContactId);
+				if (event.iInt != KErrNotFound)
+					{
+					event.iEventType=TContactViewEvent::EItemAdded;
+					event.iContactId=aEvent.iContactId;
+					NotifyObservers(event);
+					}
+				break;
+			case EContactDbObserverEventContactDeleted:
+				if(aEvent.iContactId == KNullContactId)// KNullContactId indicates a bulk delete 
+					{
+					SetState(EInitializing); // Use initializing state to avoid ESortOrderChanged event being sent to observers.
+					SafeResort();
+					}
+				else
+					{
+					event.iInt=RemoveL(aEvent.iContactId);
+					if (event.iInt != KErrNotFound)
+						{
+						event.iEventType=TContactViewEvent::EItemRemoved;
+						event.iContactId=aEvent.iContactId;
+						NotifyObservers(event);
+						}
+					}
+				break;
+			case EContactDbObserverEventGroupDeleted:
+			case EContactDbObserverEventOwnCardDeleted:
+				event.iInt=RemoveL(aEvent.iContactId);
+				if (event.iInt != KErrNotFound)
+					{
+					event.iEventType=TContactViewEvent::EItemRemoved;
+					event.iContactId=aEvent.iContactId;
+					NotifyObservers(event);
+					}
+				break;
+			case EContactDbObserverEventUnknownChanges:
+			case EContactDbObserverEventCurrentDatabaseChanged:
+				SetState(EInitializing); // Use initializing state to avoid ESortOrderChanged event being sent to observers.
+				SafeResort();
+				break;
+			case EContactDbObserverEventSortOrderChanged: // event is not currently used
+				SetState(ENotReady);
+				SafeResort();
+				break;
+			case EContactDbObserverEventTablesClosed:
+				if (iState == EReady)
+					{
+					SetState(ENotReady);
+					}
+				break;
+			case EContactDbObserverEventTablesOpened:
+				// re-read database and sort
+				SafeResort();
+				break;
+
+			case EContactDbObserverEventNull:
+			case EContactDbObserverEventUnused:
+			case EContactDbObserverEventRecover:
+			case EContactDbObserverEventCompress:
+			case EContactDbObserverEventRollback:
+			case EContactDbObserverEventTemplateChanged:
+			case EContactDbObserverEventTemplateDeleted:
+			case EContactDbObserverEventTemplateAdded:
+			case EContactDbObserverEventCurrentItemDeleted:
+			case EContactDbObserverEventCurrentItemChanged:				
+			case EContactDbObserverEventPreferredTemplateChanged:
+			case EContactDbObserverEventSpeedDialsChanged:
+			case EContactDbObserverEventRestoreBadDatabase:
+				break;
+
+			// these events should not come here, but be dealt with at the top of HandleDatabaseEventL
+			case EContactDbObserverEventBackupBeginning:
+			case EContactDbObserverEventRestoreBeginning:
+			case EContactDbObserverEventBackupRestoreCompleted:
+				break;
+				
+			default:
+				ASSERT(EFalse);
+			}
+		}
+	}
+
+TInt CContactLocalView::SortCallBack()
+	{
+	TInt ret=KErrNotFound;
+	TRAPD(err, ret = DoReadIncrementL());
+
+#if defined(__VERBOSE_DEBUG__)
+	if (err)
+		{
+		RDebug::Print(_L("[CNTMODEL] CContactLocalView{ViewPrefs = 0x%08X} . . . DoReadIncrementL ERROR %i\r\n"),
+			iViewPreferences, err);
+		}
+	else
+		{
+		RDebug::Print(_L("[CNTMODEL] CContactLocalView{ViewPrefs = 0x%08X} . . . DoReadIncrementL returned %i\r\n"),
+			iViewPreferences, ret);
+		}
+#endif
+
+	if(err!=KErrNone)
+		{
+		ret=err;
+		}
+	if (ret<0)
+		{
+		// There was an error, so notify observers and stop any further callbacks.
+		NotifySortError(ret);
+		return KSortFinished;
+		}
+	if (ret==0)
+		{
+		//Read Has Finished.
+#ifdef __PROFILE_SORT__
+		RDebug::ProfileEnd(1);
+
+		RDebug::ProfileStart(2);
+#endif
+
+		// is there a View Sort ECOM plug-in present?
+		CViewContactSortPlugin*	sortPluginImpl = SortPluginImpl();
+
+		if (sortPluginImpl)
+			{
+			// prepare View Sort plug-in
+			ret = sortPluginImpl->SortStart(CViewContactSortPlugin::ESortStartFull, iContacts.Count());
+
+			if (ret < 0)
+				{
+				return ret;
+				}
+			}
+
+		// customised array sort implementation
+		TRAP(err, ContactsArraySortL());
+
+		//Sort Has Finished.
+		if (sortPluginImpl)
+			{
+			sortPluginImpl->SortCompleted();
+			}
+
+
+#ifdef __PROFILE_SORT__
+
+		RDebug::ProfileEnd(2);
+		TProfile profile[3];
+		RDebug::ProfileResult(profile,1,3);
+
+		RDebug::Print(_L("[CNTMODEL] CntModel View, , %u, %u, Finished sort total, %u us\n"), 
+			static_cast<TUint>(RProcess().Id()), static_cast<TUint>(RThread().Id()),
+			profile[1].iTime + profile[0].iTime);
+
+		RDebug::Print(_L("[CNTMODEL] CntModel View, , , , Data Read time, %u us\n"), profile[0].iTime);
+		RDebug::Print(_L("[CNTMODEL] CntModel View, , , , Data Sort time, %u us\n"), profile[1].iTime);
+		RDebug::Print(_L("[CNTMODEL] CntModel View, , , , Compare time, %u us\n"), profile[2].iTime);
+
+#endif
+
+		// sort finished, change state, allow for 2nd pass for ICC entries
+		TInt result = iAsyncSorter->SortComplete();
+
+		if (iState != EInitializing)
+			{
+			//The view has just been re-sorted notifiy observers ESortOrderChanged
+			iState = EReady;
+			NotifyObservers(TContactViewEvent(TContactViewEvent::ESortOrderChanged));
+			HandleOutstandingEvents();
+			return result;
+			}
+		// Sorted for the first time, notifiy ready
+		SetState(EReady);
+		return result;
+		}
+	// There's more reading to be done, so request another callback.
+	return KSortCallAgain;
+	}
+
+TInt CContactLocalView::DoReadIncrementL()
+	{
+#if defined(__VERBOSE_DEBUG__)
+	RDebug::Print(_L("[CNTMODEL] CContactLocalView{ViewPrefs = 0x%08X}::DoReadIncrement()"), iViewPreferences);
+#endif
+
+	// what contacts are we adding to the View?
+	const TContactViewPreferences viewPrefs = iAsyncSorter->SortViewPreferences();
+
+	if(iViewIterator == NULL)
+		{
+		if(iFactory == NULL)
+			{
+			iFactory = const_cast<CContactDatabase&>(iDb).FactoryL();
+			}
+		MLplViewIteratorManager& manager = iFactory->GetViewIteratorManagerL();
+		iViewIterator = new (ELeave) CViewIterator(manager,*iTextDef,viewPrefs);
+		iViewIterator->GoFirstL();
+		}
+	TInt i(0);
+	// process a chunk of contacts
+	CViewContact* contact;
+	for(;i<KNumberOfContactsPerChunk;++i)
+		{
+		contact = iViewIterator->NextItemL();
+		if(contact == NULL)
+			{
+			break; // No more contacts so quick exit
+			}
+		else if(!ContactCorrectType(contact->ContactTypeUid(),viewPrefs))
+			{
+			delete contact;
+			}
+		else
+			{
+			CleanupStack::PushL(contact);
+			if(IsContactSortable(*contact,iViewPreferences))
+				{
+				iContacts.AppendL(contact);
+				CleanupStack::Pop(contact);
+				}
+			else if(iViewPreferences & (EUnSortedAtBeginning | EUnSortedAtEnd))
+				{
+				// unsortable contacts go at the end or beginning
+				iUnSortedContacts.AppendL(contact);
+				CleanupStack::Pop(contact);
+				}
+			else
+				{
+				CleanupStack::PopAndDestroy(contact);
+				}
+			}	
+		}
+	if(i== KNumberOfContactsPerChunk)
+		{
+		// Loop did not break so more contacts
+		return ETrue;
+		}
+	else
+		{
+		// Loop break so no more contacts
+		return EFalse;
+		}
+	}
+
+void CContactLocalView::ContactsArraySortL()
+	{
+
+	// HeapSort (stolen from RPointerArrayBase)
+	TInt ss = iContacts.Count();
+	if (ss>1)
+		{
+		TInt sh = ss>>1;
+		FOREVER
+			{
+			CViewContact* si;
+			if (sh!=0)
+				{
+				// make heap
+				--sh;
+				si = iContacts[sh];
+				}
+			else
+				{
+				// sort heap
+				--ss;
+				si = iContacts[ss];
+				iContacts[ss] = iContacts[0];
+				if (ss==1)
+					{
+					iContacts[0] = si;
+					break;
+					}
+				}
+
+			// sift down
+			TInt ii = sh;
+			TInt jj = sh;
+			FOREVER
+				{
+				jj = (jj+1)<<1;
+				if (jj>=ss || CompareContactsAndIdsL(*iContacts[jj-1],*iContacts[jj])>0 )
+					--jj;
+				if (jj>=ss || CompareContactsAndIdsL(*iContacts[jj],*si)<=0 )
+					break;
+				iContacts[ii] = iContacts[jj];
+				ii = jj;
+				}
+			iContacts[ii]=si;
+			}
+		}
+
+	}
+
+
+/**
+@internalComponent
+*/
+void CContactLocalView::SetState(TState aState)
+	{
+	switch (iState)
+		{
+		case EInitializing:
+		case ENotReady:
+			ASSERT(aState==EReady);
+			iState=EReady;
+			NotifyObservers(TContactViewEvent(TContactViewEvent::EReady));
+			HandleOutstandingEvents();
+			break;
+		case EReady:
+			ASSERT(aState==ENotReady || aState==EInitializing);
+			// ensure sort error is reset
+			iExtension->iError = KErrNone;
+			iState=aState;
+			NotifyObservers(TContactViewEvent(TContactViewEvent::EUnavailable));
+			break;
+		default:
+			ASSERT(EFalse);
+		}
+	}
+
+
+void CContactLocalView::HandleOutstandingEventL()
+	{
+	TContactDbObserverEvent event = iOutstandingEvents[0];
+	iOutstandingEvents.Remove(0);
+	HandleDatabaseEventL(event);
+	}
+
+void CContactLocalView::HandleOutstandingEvents()
+	{
+	while (iOutstandingEvents.Count() > 0)
+		{
+		// loop through as many events as possible in the one Trap harness
+		TRAP_IGNORE(HandleOutstandingEventL());
+		// if HandleDatabaseEventL left we must remove the event
+		}
+	}
+
+TContactViewPreferences CContactLocalView::ContactViewPreferences()
+/** Gets the view preferences, as set during construction.
+
+@return The view preferences. */
+	{
+	return iViewPreferences;
+	}
+
+const RContactViewSortOrder& CContactLocalView::SortOrderL() const
+/** Gets the sort order, as set during construction.
+
+This function cannot leave.
+
+@return The sort order. */
+	{
+	return iSortOrder;
+	}
+
+/*
+ * Notify observers that view construction failed.
+ * The error is stored so that if another client tries to open the view
+ * they will receive the same error.
+ * @param aError Leave code from CIdleContactSorter::RunL
+ */
+void CContactLocalView::NotifySortError(TInt aError)
+	{
+	iExtension->iError = aError;
+	NotifyObservers(TContactViewEvent(TContactViewEvent::ESortError, aError));
+	}
+
+/*
+ * This is a reserved virtual exported function that is used for BC proofing 
+ * against present and future additions of new exported virtual functions.
+ @return Any return values of the helper methods called from this function or NULL.
+*/
+EXPORT_C TAny* CContactLocalView::CContactViewBase_Reserved_1(TFunction aFunction,TAny* aParams)
+	{
+	return CContactViewBase::CContactViewBase_Reserved_1(aFunction,aParams);
+	}
+
+
+/*
+ * Factory constructor.
+ * @since 7.0
+ * @param aView Reference to CContactLocalView object
+ */
+CIdleContactSorter* CIdleContactSorter::NewL(CContactLocalView& aView, MLplPersistenceLayerFactory& aFactory)
+	{
+	CIdleContactSorter* self = new (ELeave) CIdleContactSorter(aView, aFactory);
+	CleanupStack::PushL(self);
+	self->ConstructL();
+	CleanupStack::Pop(self);
+	return self;
+	}
+
+/* Destructor */
+CIdleContactSorter::~CIdleContactSorter()
+	{
+	Cancel();
+
+	if (iPhbkSyncWatcher)
+		{
+		iPhbkSyncWatcher->RemovePhbkObserver(*this);
+		ReleasePhbkSyncWatcher();
+		}
+	}
+
+/* Cancel any active requests to the phonebook synchroniser */
+void CIdleContactSorter::DoCancel()
+	{
+	// Nothing to do.
+	}
+
+/** 
+ * Uses a simple state machine, initial iSortState is set by Start() to either
+ * EInsertSortFinal or EWaitingForInitialICCReady
+ *
+ * Either Insert Sort all or part of the requested view.
+ * (CIdle::RunL calls back to the Insert Sort code.)
+ *    State
+ *    EInsertContactsOnlyIccLocked		  insert Contacts only (in a mixed view)
+ *        goes to EContactsReadyWaitICCUnlock
+ *    EInsertSortFinal                    insert all Contacts & ICC entries, or
+ *        goes to ESortDone               add ICC entries to mixed view
+ *                                        (iSortView specifies which)
+ * Or wait for Phonebook Synchroniser to either finish or fail
+ *    (failure other than SIM Locked causes a Sort Error) 
+ *    State
+ *    EWaitingForInitialICCReady          the view has nothing in: Phonebook
+ *        goes to EInsertSortFinal        Synchronised allows full view to be available;
+ *        or EInsertContactsOnlyIccLocked SIM Locked allows a view without ICC entries to 
+ *                                        accessible
+ *    EContactsReadyWaitICCUnlock         SIM was previously found to be locked, if/when
+ *        goes to EInsertSortFinal        Phonebook Synchroniser completes we can merge in
+ *                                        requested ICC entries
+ *
+ * The check whether the phonebook synchroniser is in a cache-valid state:-
+ *
+ * This check is done by making a async request to be completed when the 
+ * phbksync cache state has changed, checking the current cache state and 
+ * if the cache is valid already cancelling the request.
+ * (The cancelled request will complete, causing RunL to run again.)
+ * If there was a phbksync error check the error code, if it is not due to the
+ * SIM card being locked then Leave.
+ */
+void CIdleContactSorter::RunL()
+	{
+#if defined(__VERBOSE_DEBUG__)
+	RDebug::Print(_L("[CNTMODEL] CIdleContactSorter{RequestedView = 0x%08X, SortView = 0x%08X}::RunL()\r\n"),
+		iRequestedView, iSortView);
+	TSorterState oldSortState = iSortState;		// for debug messages only
+#endif
+
+	User::LeaveIfError(iStatus.Int());
+
+	// either sort or wait for ICC ready / phonebook synch state change
+	switch (iSortState)
+		{
+		// states that are sorting all or part of view
+	case EInsertSortFinal:				// full insert sort or Phonebook Synched so add ICC entries
+	case EInsertContactsOnlyIccLocked:	// insert Contacts for now, then wait for SIM to be unlocked
+		// do slice of full / Contacts only /ICC only insert sort
+		if (iView.SortCallBack() == KSortCallAgain)
+			{ // CAsyncOneShot::Call()
+			Call();
+			}
+		break;
+
+		// states that are waiting for a phonebook sync event
+	case EWaitingForInitialICCReady:	// ICC entries in view, waiting for Phonebook Synch state change
+		// ICC entries are included in view:
+		if (iPhbkSyncWatcher->PhonebooksReady() > 0)
+			{
+			// ICC sync complete - can immediately sort everything
+			ChangeSortState(EInsertSortFinal);
+			}
+		else if (iPhbkSyncWatcher->PhonebooksWaiting() > 0)
+			{
+			// SIM card is locked and, this is the first time we've seen this
+
+			// Insert Contacts (if wanted) into View now
+			// Afterwards we will wait again for SIM to unlock & Phonebook Synch to complete
+			if(iRequestedView & EICCEntriesAndContacts)
+				{
+				// insert/sort view, but without the requested ICC entries
+				iSortView = static_cast<TContactViewPreferences>(iSortView & ~EICCEntriesAndContacts);
+				ChangeSortState(EInsertContactsOnlyIccLocked);
+				}
+			else 
+				{
+				// only ICC entries were wanted in the first place
+				// so make the (empty) View Ready
+				const_cast<CContactLocalView&>(iView).SetState(CContactLocalView::EReady);
+				// now wait for SIM to unlock & Phonebook Synch to complete
+				ChangeSortState(EContactsReadyWaitICCUnlock);
+				}
+			}
+		else
+			{
+			// synchronisation finished with an error?
+			User::LeaveIfError(iPhbkSyncWatcher->PhonebookSyncError());
+			}
+
+		// otherwise wait for a Phonebook Synch event
+		break;
+
+	case EContactsReadyWaitICCUnlock:	// when SIM is unlocked add ICC Entries to this view
+		// ICC entries are included in view:
+		if (iPhbkSyncWatcher->PhonebooksReady() > 0)
+			{
+			// ICC sync complete - can sort everything
+			ChangeSortState(EInsertSortFinal);
+
+			// add requested ICC entries into the sorted view
+			iSortView = STATIC_CAST(TContactViewPreferences, (iSortView & ~EContactsOnly) | EICCEntriesOnly);
+			const_cast<CContactLocalView&>(iView).SetState(CContactLocalView::ENotReady);
+
+			// another pass through contacts database is needed
+			// ResetSortL should not Leave (especially as it must have worked previously)
+			const_cast<CContactLocalView&>(iView).ResetSortL();
+			}	
+
+		// otherwise wait for a Phonebook Synch event
+		break;
+
+	case ESortAllDone: // shouldn't have come back here
+	default:
+		Panic(ECntPanicViewSorterStateMachine);
+		break;
+		}
+
+#if defined(__VERBOSE_DEBUG__)
+	if (oldSortState != iSortState)
+		{
+		switch (iSortState)
+			{
+			case EInsertSortFinal:
+				RDebug::Print(_L("[CNTMODEL] {RequestedView = 0x%08X} * * * new Sort State = EInsertSortFinal, SortView = 0x%08X\r\n"),
+					iRequestedView, iSortView);
+				break;
+			case EInsertContactsOnlyIccLocked:
+				RDebug::Print(_L("[CNTMODEL] {RequestedView = 0x%08X} * * * new Sort State = EInsertContactsOnlyIccLocked, SortView = 0x%08X\r\n"),
+					iRequestedView, iSortView);
+				break;
+			case EWaitingForInitialICCReady:
+				RDebug::Print(_L("[CNTMODEL] {RequestedView = 0x%08X} * * * new Sort State = EWaitingForInitialICCReady\r\n"),
+					iRequestedView);
+				break;
+			case EContactsReadyWaitICCUnlock:
+				RDebug::Print(_L("[CNTMODEL] {RequestedView = 0x%08X} * * * new Sort State = EContactsReadyWaitICCUnlock, SortView = 0x%08X\r\n"),
+					iRequestedView, iSortView);
+				break;
+			case ESortAllDone:
+				RDebug::Print(_L("[CNTMODEL] {RequestedView = 0x%08X} * * * new Sort State = ESortAllDone\r\n"), iRequestedView);
+				break;
+			}
+		}
+	RDebug::Print(_L("[CNTMODEL] [Unsorted Contacts = %i, Sorted Contacts = %i, IsActive = %i]\r\n"),
+		const_cast<CContactLocalView&>(iView).iUnSortedContacts.Count(),
+		const_cast<CContactLocalView&>(iView).iContacts.Count(),
+		IsActive());
+#endif
+
+	}
+
+
+void CIdleContactSorter::ChangeSortState(TSorterState aNewSortState)
+	{
+#if defined(__VERBOSE_DEBUG__)
+	RDebug::Print(_L("[CNTMODEL] CIdleContactSorter{RequestedView = 0x%08X, SortView = 0x%08X}::ChangeSortState(%i)\r\n"),
+		iRequestedView, iSortView, static_cast<TInt>(aNewSortState));
+#endif
+	// new state
+	iSortState = aNewSortState;
+	// make the active object to run, CAsyncOneShot::Call()
+	Call();
+	}
+
+
+/* 
+ * Handle any leave during CIdleContactSorter::RunL. 
+ * The local view is informed of that the view construction failed.
+ * It will broadcast an ESortError view event to all clients of this view.
+ * 
+ * @param aError Leave code from RunL
+ */
+TInt CIdleContactSorter::RunError(TInt aError)
+	{
+#if defined(__VERBOSE_DEBUG__)
+	RDebug::Print(_L("[CNTMODEL] CIdleContactSorter{RequestedView = 0x%08X, SortView = 0x%08X}::RunError(error = %i)\r\n"),
+		iRequestedView, iSortView, aError);
+#endif
+	if ((aError != KErrCancel) && (iSortState != ESortAllDone))
+		{
+		const_cast<CContactLocalView&>(iView).NotifySortError(aError);
+		iSortState = ESortAllDone;
+		}
+	return KErrNone;
+	}
+
+
+TInt CIdleContactSorter::SortComplete()
+/**
+ * Sort or partial sort completed, decide if there is more to do:
+ * EInsertSortFinal -> ESortAllDone
+ * EInsertContactsOnlyIccLocked -> EContactsReadyWaitICCUnlock (wait for SIM to become unlocked)
+ *
+ * return KSortCallAgain if there is more work to do, KSortFinished otherwise
+ */
+	{
+	if(iSortState == EInsertContactsOnlyIccLocked)
+		{
+		// we are now waiting for phbksync, so that we can add ICC entries
+		iSortState = EContactsReadyWaitICCUnlock;
+#if defined(__VERBOSE_DEBUG__)
+		RDebug::Print(_L("[CNTMODEL] * * * * * SortComplete: New Sort State = EContactsReadyWaitICCUnlock\r\n"));
+#endif
+		// CIdleContactSorter has more to do
+		return KSortCallAgain;
+		}
+
+	// CIdleContactSorter all done
+	iSortState = ESortAllDone;
+#if defined(__VERBOSE_DEBUG__)
+	RDebug::Print(_L("[CNTMODEL] * * * * * SortComplete: New Sort State = ESortAllDone\r\n"));
+#endif
+	return KSortFinished;
+	}
+
+/*
+ Initialise Idle Contact Sorter for a new sort
+
+ Re-init iSortView - the view filter for the Insert Sort
+ Decide the initial iSortState for RunL:
+   Contacts only view -> EInsertSortFinal
+   ICC entries included -> EWaitingForInitialICCReady
+ */
+ void CIdleContactSorter::Start()
+	{
+	// initially we will try to insert sort everything requested
+	iSortView = iRequestedView;
+
+	if (iPhbkSyncWatcher)
+		{
+		// ICC entries included in view, must wait for Phonebook Synch
+		iSortState = EWaitingForInitialICCReady;
+		}
+	else
+		{
+		// Only Contacts wanted in view, we can Sort straight away
+		iSortState = EInsertSortFinal;
+		}
+
+#if defined(__VERBOSE_DEBUG__)
+	RDebug::Print(
+		(iSortState == EInsertSortFinal) ? 
+			_L("[CNTMODEL] CIdleContactSorter{RequestedView = 0x%08X}::Start() Sort State = EInsertSortFinal\r\n") :
+			_L("[CNTMODEL] CIdleContactSorter{RequestedView = 0x%08X}::Start() Sort State = EWaitingForInitialICCReady\r\n"),
+		iRequestedView);
+#endif
+
+	// set Active for the first time, CAsyncOneShot::Call()
+	Call();
+	}
+
+
+/*
+ Stop any sort that is already in progress
+ */
+void CIdleContactSorter::Stop()
+	{
+	if (iSortState != ESortAllDone)
+ 		{
+		// stop sorting
+		iSortState = ESortAllDone;
+		Cancel();
+ 		}
+	}
+
+
+/* 
+ * Second phase construction.
+ * Copy the View's requested preferences, get link to ICC phonebook watcher
+ */
+void CIdleContactSorter::ConstructL()
+	{
+	iRequestedView = CONST_CAST(CContactLocalView&,iView).ContactViewPreferences();
+
+	// is ICC phonebook sync expected?
+	if (iRequestedView & (EICCEntriesOnly | EICCEntriesAndContacts))
+		{
+		iPhbkSyncWatcher = GetPhbkSyncWatcherL();
+
+		// observe ICC sync events
+		iPhbkSyncWatcher->AddPhbkObserverL(*this);
+		}
+	}
+
+ /*
+ Utility method to return a reference to the ICC synchroniser watcher.
+ Always returns a pointer to the real phonebook synchroniser plugin.
+ It is assumed that Phonebook synchronising server never creates the view,
+ so the deadlock in not possible and the dummy plugin is not required.
+
+ @internalTechnology
+ @leave KErrNotSupported if contact synchroniser plug-in cannot be found
+ @leave KErrNoMemory if not enough memory
+ @return Pointer to CContactPhbkSyncWatcher instance.
+ */
+CContactPhbkSyncWatcher* CIdleContactSorter::GetPhbkSyncWatcherL()
+	{
+	if (!iPhbkSyncWatcher)
+		{
+		iPhbkSyncWatcher = CContactPhbkSyncWatcher::NewL(iFactory.GetContactSynchroniserL(KMaxTUint32)); //Always use the Contact Synchroniser
+		}
+	return iPhbkSyncWatcher;
+	}
+
+
+void CIdleContactSorter::ReleasePhbkSyncWatcher()
+	{
+	if (iPhbkSyncWatcher)
+		{
+		if (iPhbkSyncWatcher->ObserverCount() == 0)
+			{
+			delete iPhbkSyncWatcher;
+			iPhbkSyncWatcher = NULL;
+			}
+		}
+	}
+
+
+/* Constructor */
+  CIdleContactSorter::CIdleContactSorter(CContactLocalView& aView, MLplPersistenceLayerFactory& aFactory) 
+  : CAsyncOneShot(CActive::EPriorityLow), iView(aView), iFactory(aFactory)
+	{
+	}
+
+/* 
+ * Determines whether view events should be queued.
+ *
+ * View events are only queued when the ICC has been synchronised. This prevents
+ * duplicate contacts in an ICC view because add events are not queued until the 
+ * SIM is fully synchronised. 
+ * 
+ * See LUD-5EBHZF "ICC contacts view broadcasts add item events after view is 
+ * ready" for more detail.
+ * 
+ * @return ETrue, if view events should be queued. EFalse, otherwise
+ */
+TBool CIdleContactSorter::QueueViewEvents() const
+	{
+	// Initial wait for phonebook synch (i.e. waiting for ICC ready or locked) ?
+	if(iSortState == EWaitingForInitialICCReady)
+		{
+		return EFalse;
+		}
+	return ETrue;
+	}
+
+TContactViewPreferences CIdleContactSorter::SortViewPreferences() const
+/**
+ * Current View Preferences for insert sort in to View
+ *
+ * May be a subset of the requested View.
+ * If the SIM card is locked this will initially be a View without ICC entries.
+ * If the SIM becomes unlocked a second pass then picks out ICC entries only.
+ *
+ */
+	{
+	return iSortView;
+	}
+
+
+TBool CIdleContactSorter::InsertViewPreferences(TContactViewPreferences &aInsertView) const
+/**
+ * Modifies View Preferences for inserting into View
+ *
+ * May be a subset of the requested View:
+ * If a Mixed (Contacts & ICC view) is requested and the Phonebook Synch has NOT
+ * completed then only Contacts entries are added to the view. When the PhoneBook Synch 
+ * completes all ICC entries will at the same time.
+ *
+ */
+	{
+	TBool okayToInsert = ETrue;
+
+	switch (iSortState)
+		{
+	case EInsertSortFinal:				// full insert sort or Phonebook Synched so add ICC entries
+	case ESortAllDone:
+		// view is finished or finishing, can Insert any contact
+		break;
+
+	case EInsertContactsOnlyIccLocked:	// insert Contacts for now, then wait for SIM to be unlocked
+		// only Contacts can be inserted now, no ICC entries
+		aInsertView = iSortView;
+		break;
+
+	case EWaitingForInitialICCReady:	// ICC entries in view, waiting for Phonebook Synch state change
+		// Waiting for initial ICC Synch result, insert nothing
+		okayToInsert = EFalse;
+		break;
+
+	case EContactsReadyWaitICCUnlock:	// when SIM is unlocked add ICC Entries to this view
+		if (aInsertView & EICCEntriesOnly)
+			{
+			okayToInsert = EFalse;		// can't insert ICC entries yet
+			}
+		else
+			{
+			aInsertView = iSortView;	// only Insert Contacts
+			}
+		break;
+		}
+
+	return okayToInsert;
+	}
+
+TBool CContactLocalView::ContactCorrectType(TUid aType,TContactViewPreferences aTypeToInclude)
+	{
+	TBool correctType(EFalse);
+	if (aType==KUidContactCard || aType==KUidContactOwnCard)
+		{
+		// Ignore Unsorted Contacts flags & White Space flag
+		// catch non- contact views
+		// Should be EContactsOnly, EContactAndGroups & EICCEntriesAndContacts
+		if (0 == ((aTypeToInclude & ~(ESingleWhiteSpaceIsEmptyField | EIgnoreUnSorted | EUnSortedAtBeginning | EUnSortedAtEnd))
+				& (EGroupsOnly | EICCEntriesOnly))) // Ignore 'UnSorted' flags, exclude Groups Only & ICC Only 
+			{
+			correctType = ETrue;
+			}
+		}
+	else if (aType==KUidContactGroup)
+		{
+		if (aTypeToInclude & (EGroupsOnly | EContactAndGroups))
+			{
+			correctType = ETrue;
+			}
+		}
+	else if (aType == KUidContactICCEntry)
+		{
+		if (aTypeToInclude & (EICCEntriesOnly | EICCEntriesAndContacts))
+			{
+			correctType = ETrue;
+			}
+		}
+	return correctType;
+	}
+
+
+void CIdleContactSorter::ContactPhbkSyncEventHandler(TPhonebookState aPhbkState)
+	{
+#if defined(__VERBOSE_DEBUG__)
+	RDebug::Print(_L("[CNTMODEL] CIdleContactSorter{RequestedView = 0x%08X, SortView = 0x%08X}::ContactPhbkSyncEventHandler\r\n"),
+		iRequestedView, iSortView);
+#endif
+	switch (aPhbkState)
+		{
+		case EIccPhbkNotSynchronised:
+			/* Initial state, or ICC card has 'gone away' (e.g. ICC is resetting). */
+			// no action - may want to act on this in future
+			break;
+
+		case EIccPhbkSynchronised:	// ICC Phonebook has completed synchronisation.
+
+		case EIccWaitForPhbkToBeReady:	// Sync failed due to ICC being locked or not ready.
+
+		case EIccPhbkSyncError:		//	Sync with Phbk Server failed.
+
+#if defined(__VERBOSE_DEBUG__)
+			RDebug::Print(aPhbkState == EIccPhbkSynchronised ? _L("[CNTMODEL]     state = ICC phonebook synchronised)\r\n") :
+				aPhbkState == EIccWaitForPhbkToBeReady ? _L("    state = ICC phonebook Locked)\r\n") :
+				 _L("    state = ICC phonebook Sync Error)\r\n"));
+#endif
+			// in a sorting state where we care?
+			if ((iSortState == EWaitingForInitialICCReady) || (iSortState == EContactsReadyWaitICCUnlock))
+				{
+				// let state machine in RunL() deal with the event
+				if (!IsActive())
+					{ // CAsyncOneShot::Call()
+					Call();
+					}
+				}
+			break;
+		}
+	}
+
+