/* * Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies). * Contact: http://www.qt-project.org/legal * 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 #include "CNTSTD.H" #include #include #include "cntviewprivate.h" #include #include "persistencelayer.h" #include "cviewiterator.h" #ifdef SYMBIAN_ENABLE_SPLIT_HEADERS #include #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(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(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(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=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(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(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=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, // codescanner::trapeleave: CViewIterator hasn't NewL function and ignore the medium issue. if( iFactory == NULL ) { iFactory = const_cast( 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;sortIndexAppendL(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(RProcess().Id()), static_cast(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(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(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(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(RProcess().Id()), static_cast(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(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(;iNextItemL(); 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(iSortView & ~EICCEntriesAndContacts); ChangeSortState(EInsertContactsOnlyIccLocked); } else { // only ICC entries were wanted in the first place // so make the (empty) View Ready const_cast(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(iView).SetState(CContactLocalView::ENotReady); // another pass through contacts database is needed // ResetSortL should not Leave (especially as it must have worked previously) const_cast(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(iView).iUnSortedContacts.Count(), const_cast(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(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(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) { if (!(aTypeToInclude & (EGroupsOnly | EICCEntriesOnly))) { correctType = ETrue; } } else if (aType == KUidContactOwnCard) { if (!(aTypeToInclude & (EGroupsOnly | EICCEntriesOnly | EContactCardsOnly))) { 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; } }