--- /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;
+ }
+ }
+
+