--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/srsf/nssvascontacthdlr/src/nssvasccontacthandlerimpl.cpp Thu Dec 17 08:46:30 2009 +0200
@@ -0,0 +1,3429 @@
+/*
+* Copyright (c) 2005-2006 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: Responsible for maintaining synchronization between the contact DB
+* and the VAS DB
+*
+*/
+
+
+/** @todo Read the whole contact in one go.
+
+ Currently Contact handler usually opens the same contact (ReadContactL) two to
+ three times in order to read its data (mostly often three times).
+ One is in GetContactNickname
+ Second in GetContactTitle
+ Third in DoAddNamesL when deciding what number field should the voice tag
+ be trained for
+ If we could unite these reads, "reading contacts" phase will be 2-3 times faster.
+ It would make about 10-20% improvement for the training of some 300 contacts */
+
+// INCLUDE FILES
+#include "nssvasccontacthandlerimpl.h"
+#include "nssvasctrainingparameters.h"
+#include <nssvascvasdbmgr.h>
+#include <centralrepository.h>
+#include <f32file.h>
+#include <bautils.h>
+#include <barsc.h>
+#include <barsread.h>
+#include <sysutil.h>
+#include "srsfprivatecrkeys.h"
+
+// Publish & Subscribe of the contact handler activity
+#include <nssvascoreconstant.h>
+
+#include <vascvpbkhandler.h>
+#include <featmgr.h>
+#include "nssvasdatasyncwatcher.h"
+#include "nsschbackupobserver.h"
+#include <StringLoader.h>
+#include <nssvascontacthandler.rsg>
+#include <data_caging_path_literals.hrh>
+#include "rubydebug.h"
+
+// CONSTANTS
+// Name of the context in the SIND DB. Voice tags for contacts are saved there
+_LIT( KNssCHNameDialContext, "NAMEDIAL" );
+
+// Languages which use lastname-firstname order
+const TLanguage KSwappedLanguages[] =
+ {
+ ELangHungarian, // 17
+ ELangTaiwanChinese, // 29
+ ELangHongKongChinese, // 30
+ ELangJapanese, // 32
+ ELangPrcChinese, // 31
+ ELangKorean, // 65
+ ELangVietnamese // 96
+ };
+
+// Number of languages that use lastname-firstname order
+const TInt KNumberOfSwappedLanguages = sizeof( KSwappedLanguages ) / sizeof ( TLanguage );
+
+
+// Maximal number of retrials to save tags
+const TInt KVASCHMaxRetry = 2;
+
+// Size of the event 'pack'. Tags are added and deleted in groups.
+// KMaxNamesAtOnce is the maximal size of a group
+const TInt KMaxNamesAtOnce = 100;
+
+// Initial value of the time interval used to detect periodic action. In
+// microseconds. If KEventMomentsSize events happened within the
+// KInitialPeriodicActionInterval, we assume, that periodic action has started
+const TInt KInitialPeriodicActionInterval = 15000000;
+
+// Time interval used to periodically check if there are pending
+// events to process. In microseconds
+const TInt KPeriodicTimerInterval = 1000000;
+
+// If there are this many consecutive full resyncs initiated by errors during
+// handling normal changes (or during previous full resync), there is probably
+// something consistently wrong at the lower layers.
+// Full resync trials (and the whole contact handler) should be disabled until
+// reboot after KMaxConsecutiveErrorFullResync consecutive unsuccessful trials
+const TInt KMaxConsecutiveErrorFullResync = 2;
+
+// List of phonebook fields, that are supported by contact handler.
+// I.e. fields, that can be dialed and voice tagged
+/** @todo Consider using information from phonebook about which fields contain numbers
+and using the following lists for priorities only */
+const TPbkFieldId KTaggableFields[] =
+ {
+ EPbkFieldIdPhoneNumberMobile,
+ EPbkFieldIdPhoneNumberGeneral,
+ EPbkFieldIdPhoneNumberStandard,
+ EPbkFieldIdPhoneNumberHome,
+ EPbkFieldIdPhoneNumberWork,
+ EPbkFieldIdPhoneNumberVideo,
+ EPbkFieldIdVOIP,
+ EPbkFieldIdXsp
+ };
+const TInt KTaggableFieldsCount = sizeof( KTaggableFields ) / sizeof ( TPbkFieldId );
+
+// List of phonebook fields that are supported by video extension
+const TPbkFieldId KVideoExtensionFields[] =
+ {
+ EPbkFieldIdPhoneNumberVideo,
+ EPbkFieldIdPhoneNumberMobile,
+ EPbkFieldIdPhoneNumberGeneral,
+ EPbkFieldIdPhoneNumberHome,
+ EPbkFieldIdPhoneNumberWork
+ };
+const TInt KVideoExtensionFieldsCount = sizeof( KVideoExtensionFields ) / sizeof ( TPbkFieldId );
+
+// List of phonebook fields that are supported by message extension
+const TPbkFieldId KMessageExtensionFields[] =
+ {
+ EPbkFieldIdPhoneNumberMobile,
+ EPbkFieldIdPhoneNumberGeneral,
+ EPbkFieldIdPhoneNumberHome,
+ EPbkFieldIdPhoneNumberWork,
+ EPbkFieldIdPhoneNumberVideo
+ };
+const TInt KMessageExtensionFieldsCount = sizeof( KMessageExtensionFields ) / sizeof ( TPbkFieldId );
+
+// List of phonebook fields that are supported by VOIP extension
+const TPbkFieldId KVOIPExtensionFields[] =
+ {
+ EPbkFieldIdVOIP,
+ EPbkFieldIdXsp
+ };
+const TInt KVOIPExtensionFieldsCount = sizeof( KVOIPExtensionFields ) / sizeof ( TPbkFieldId );
+
+// Size of the client data per context. Is used to store the context language
+// In bytes
+const TInt KClientDataSize = sizeof(TNssContextClientData);
+
+
+// Number of retries to try to lock the same contact
+// 600 times should stand for 10 mins
+// -1 means trying forever
+const TInt KMaxContactLockRetrials = 600;
+
+// Interval between the ticks of the contact wait timer
+// in microseconds
+const TInt32 KContactWaitInterval = 1000000;
+
+// Bytes that should be free in the disk drive in addition to critical level
+// before operation is started (100kB)
+const TInt KIncreaseOfDatabaseSizes = 100000;
+
+// Define this if name free-order is enabled
+//#define NSSVAS_CONTACTHANDLER_FREEORDER
+// LOCAL FUNCTION DECLARATIONS
+
+
+// ================= MEMBER FUNCTIONS =======================
+
+
+#ifdef _DEBUG
+_LIT( KFile, "VasCContactHandlerImpl.cpp" );
+#endif
+
+/**
+* Is used in DoHandleEventFailedL to *asynchronously* simulate successful tag
+* deletion
+*/
+NONSHARABLE_CLASS( CSuccessfulDeletionSimulator ) : public CAsyncOneShot
+ {
+ public:
+ CSuccessfulDeletionSimulator( CNssContactHandlerImplementation& aHost );
+ ~CSuccessfulDeletionSimulator();
+ protected:
+ /** Is called by the system after we asked it to Call() closer
+ */
+ virtual void RunL();
+ private:
+ CNssContactHandlerImplementation& iHost;
+ };
+
+// -----------------------------------------------------------------------------
+// CSuccessfulDeletionSimulator::~CSuccessfulDeletionSimulator
+// Destructor
+// -----------------------------------------------------------------------------
+//
+CSuccessfulDeletionSimulator::~CSuccessfulDeletionSimulator()
+ {
+ RUBY_DEBUG0( "~CSuccessfulDeletionSimulator" );
+ if( IsAdded() )
+ {
+ RUBY_DEBUG0( "~CSuccessfulDeletionSimulator Request is still in progress, deque it" );
+ Deque();
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CSuccessfulDeletionSimulator::CSuccessfulDeletionSimulator
+// Constructor
+// -----------------------------------------------------------------------------
+//
+CSuccessfulDeletionSimulator::CSuccessfulDeletionSimulator( CNssContactHandlerImplementation& aHost ) :
+ CAsyncOneShot( CActive::EPriorityStandard ),
+ iHost( aHost )
+ {
+ //nothing
+ }
+
+// -----------------------------------------------------------------------------
+// CAppCloser::RunL
+// Will be called by the system and exits the application
+// -----------------------------------------------------------------------------
+//
+void CSuccessfulDeletionSimulator::RunL()
+ {
+ RUBY_DEBUG0( "CSuccessfulDeletionSimulator::RunL Start" );
+ if( iStatus == KErrNone )
+ {
+ CArrayPtrFlat<MNssTag>* empty = new (ELeave) CArrayPtrFlat<MNssTag>( 1 );
+ iHost.DoRemoveNamesAfterGetTagList( empty );
+ // empty will be destroyed in the DoRemoveNamesAfterGetTagList
+ }
+ else
+ {
+ RUBY_ERROR1( "CSuccessfulDeletionSimulator::RunL Unexpected iStatus [%d]", iStatus.Int() );
+ }
+ RUBY_DEBUG0( "CSuccessfulDeletionSimulator::RunL End" );
+ }
+
+
+
+// -----------------------------------------------------------------------------
+// CNssContactHandlerImplementation::CNssContactHandlerImplementation
+// C++ default constructor can NOT contain any code, that
+// might leave.
+// -----------------------------------------------------------------------------
+//
+CNssContactHandlerImplementation::CNssContactHandlerImplementation( )
+: iState( ECHInitializing ),
+ iSeparator( KNameSeparator()[0] ),
+ iEndOfPeriodicActionInterval( KInitialPeriodicActionInterval )
+ {
+ iClientData.iLanguage = ELangNone;
+
+ // initialize last RestartTimer calls to be long ago in
+ // the past (January 1st, 0 AD nominal Gregorian)
+ for( TInt i = 0; i < KEventMomentsSize; i++)
+ {
+ iEventMoments[i] = 0;
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CNssContactHandlerImplementation::ConstructL
+// EPOC second phase constructor can leave.
+// -----------------------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::ConstructL()
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::ConstructL" );
+
+ User::LeaveIfError( iFSession.Connect() );
+
+ // Sets up TLS, must be done before FeatureManager is used.
+ FeatureManager::InitializeLibL();
+ iVoIPFeatureSupported = FeatureManager::FeatureSupported( KFeatureIdCommonVoip );
+ iVideoCallFeatureSupported = FeatureManager::FeatureSupported( KFeatureIdCsVideoTelephony );
+ // Frees the TLS! Must be done after FeatureManager is used.
+ FeatureManager::UnInitializeLib();
+
+ // Before enabling event, start monitoring the PC-suite state
+ iSyncBackupWaiter = new (ELeave) CActiveSchedulerWait;
+
+ iDataSyncWatcher = CNssDataSyncWatcherFactory::ConstructDataSyncWatcherL( *this );
+
+ iBackupObserver = CNssChBackupObserver::NewL( *this );
+
+ iContactWaiter = new (ELeave) CActiveSchedulerWait;
+
+ iDeletionSimulator = new (ELeave) CSuccessfulDeletionSimulator( *this );
+
+ EnableEventHandling();
+
+ iEventArray.Reset(); // Ready to be used
+
+ TInt errProp = iBusyProperty.Define( KSINDUID, ETrainingContactsState, RProperty::EInt );
+ if( ( errProp != KErrNone ) && ( errProp != KErrAlreadyExists ) )
+ {
+ RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to define the property. Error [%d]",
+ errProp);
+ User::Leave( errProp );
+ }
+
+ // Publish contact handler as busy before the DB operations
+ errProp = iBusyProperty.Attach( KSINDUID, ETrainingContactsState );
+ if( errProp != KErrNone )
+ {
+ // KErrNotFound is also an error as we just defined the property
+ RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to attach to property. Error [%d]",
+ errProp);
+ User::Leave( errProp );
+ }
+
+ errProp = iBusyProperty.Set( EContactsTraining );
+ if ( errProp != KErrNone )
+ {
+ RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to set property. Error [%d]",
+ errProp );
+ User::Leave( errProp ) ;
+ }
+
+
+ // central repository
+ iRepository = CRepository::NewL( KCRUidSRSFSettings );
+ iRepositoryObserver = CCenRepNotifyHandler::NewL( *this, *iRepository,
+ CCenRepNotifyHandler::EIntKey,
+ KSRSFFullResyncNeeded );
+ iRepositoryObserver->StartListeningL();
+
+ //Get VAS DB manager
+ iVasDbManager = CNssVASDBMgr::NewL();
+ iVasDbManager->InitializeL();
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::ConstructL iVasDbManager initialized" );
+
+ //Get Tag Manager and Context Manager interfaces
+ iTagManager = iVasDbManager->GetTagMgr();
+ iContextManager = iVasDbManager->GetContextMgr();
+
+ // Prepare training parameters
+ iTrainingParams = CNssTrainingParameters::NewL();
+
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::ConstructL iTrainingParams prepared" );
+
+ // Set languages which have lastname-firstname order
+ for ( TInt iCounter = 0; iCounter < KNumberOfSwappedLanguages; iCounter++ )
+ {
+ User::LeaveIfError( iSwappedLanguages.Append( KSwappedLanguages[iCounter] ) );
+ }
+
+#ifdef __SINDE_TRAINING
+ // Main array for languages
+ RArray<RTrainingLanguageArray> languages;
+
+ // Languages for names
+ RArray<TLanguage> nameLanguages;
+ User::LeaveIfError( nameLanguages.Append( User::Language() ) );
+ User::LeaveIfError( nameLanguages.Append( ELangOther ) );
+ User::LeaveIfError( languages.Append( nameLanguages ) );
+
+ // Languages for extensions
+ RArray<TLanguage> extLanguages;
+ User::LeaveIfError( extLanguages.Append( User::Language() ) );
+ User::LeaveIfError( languages.Append( extLanguages ) );
+
+ // Store languages to training parameters
+ iTrainingParams->SetSindeLanguages( languages );
+
+ // Close arrays, iTrainingParams has done a copy of these
+ extLanguages.Close();
+ nameLanguages.Close();
+ languages.Close();
+
+#else
+ // Set the languages
+ RArray<TLanguage> *languages = new (ELeave) RArray<TLanguage>;
+ CleanupStack::PushL( languages );
+ // Always generate a pronunciation in UI language
+ User::LeaveIfError( languages->Append( User::Language() ) );
+ // give some extra languages, which can be selected in engine
+ User::LeaveIfError( languages->Append( ELangOther ) );
+ iTrainingParams->SetLanguages( languages );
+ CleanupStack::Pop( languages );
+#endif
+
+ // The separator between the first name and the last name
+ iTrainingParams->SetSeparator( iSeparator );
+
+#ifdef __SIND_EXTENSIONS
+
+ ReadExtensionsL();
+#endif // __SIND_EXTENSIONS
+
+ ConnectToPhonebookL();
+
+ TLitC<9> name = KNssCHNameDialContext;
+
+ // Get context.
+
+ // (1) if we have synced earlier -> we have created a context, and saved the language
+ // (2) if we haven't synced -> we need to create context
+ //
+ // In case (2), GetContext fails.
+ // We'll assume that GetContext is successful until it fails.
+ iHaveWeEverSynced = ETrue;
+
+ TInt err = iContextManager->GetContext( this, name );
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::ConstructL Requested GetContext. err [%d]", err);
+
+ if ( err != KErrNone )
+ {
+ // The same function is called, whether GetContext fails synchronously
+ // or asynchronously.
+ GetContextCompleted( NULL, KErrNotFound );
+ }
+ iDeleteTagListArray = new (ELeave) CArrayPtrFlat<MNssTag>(50);
+
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::NewL
+// Two-phased constructor.
+// ---------------------------------------------------------
+//
+CNssContactHandlerImplementation* CNssContactHandlerImplementation::NewL( )
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::NewL" );
+ CNssContactHandlerImplementation* self = new (ELeave) CNssContactHandlerImplementation();
+
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ CleanupStack::Pop( self );
+
+ return self;
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::~CNssContactHandlerImplementation
+// Destructor
+// ---------------------------------------------------------
+//
+CNssContactHandlerImplementation::~CNssContactHandlerImplementation()
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::~CNssContactHandlerImplementation FIXED" );
+
+ iFSession.Close();
+
+ iEventArray.Close(); // Close the RArray
+
+ delete iDataSyncWatcher;
+ delete iBackupObserver;
+
+ if ( iSyncBackupWaiter != NULL )
+ {
+ if( iSyncBackupWaiter->IsStarted() )
+ {
+ iSyncBackupWaiter->AsyncStop();
+ }
+ }
+ delete iSyncBackupWaiter;
+
+ if ( iContactWaiter != NULL )
+ {
+ if( iContactWaiter->IsStarted() )
+ {
+ iContactWaiter->AsyncStop();
+ }
+ }
+
+
+ delete iContactWaiter;
+ delete iDeletionSimulator;
+ delete iPeriodicHandleEventTimer;
+
+ delete iContext;
+
+ delete iVasDbManager;
+ RProperty::Delete( KSINDUID, ETrainingContactsState );
+ iBusyProperty.Close();
+
+ delete iRepositoryObserver;
+ delete iRepository;
+
+ delete iTrainingParams;
+ iTagArray.ResetAndDestroy();
+
+ for( TInt k( 0 ); k < iContactQueue.Count(); k++ )
+ {
+ delete iContactQueue[k].iTitle;
+ }
+ iContactQueue.Close();
+
+ iDelList.Close();
+ iAddList.Close();
+ iRemoveIconList.Close();
+ iVoiceTagAddedList.Close();
+ iSwappedLanguages.Close();
+
+ DisconnectFromPhonebook();
+
+#ifdef __SIND_EXTENSIONS
+
+ for ( TInt i(0); i < iExtensionList.Count(); i++ )
+ {
+ delete iExtensionList[i].iText;
+ }
+
+ iExtensionList.Close();
+#endif // __SIND_EXTENSIONS
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::HandleEventL
+// Called by phonebook handler when a contact database event
+// occurs
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::HandleEventL( const TPhonebookEvent& aEvent )
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::HandleEventL" );
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::HandleEventL(). Contact id [%d], event type [%d]", aEvent.iContactId, aEvent.iType );
+
+ if ( aEvent.iType == EContactChanged ||
+ aEvent.iType == EContactDeleted ||
+ aEvent.iType == EContactAdded ||
+ aEvent.iType == EUnknownChanges )
+ {
+ if( !CausedByVoiceTagAddition( aEvent ) )
+ {
+ // Check if operations can be finished
+ // There should be enough disk space
+ if ( NoDiskSpace() )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::HandleEventL not enough disk space, disabling event handling" );
+ DisableEventHandling();
+ }
+ else
+ {
+ TInt errProp = iBusyProperty.Set( EContactsTraining );
+ if ( errProp != KErrNone )
+ {
+ RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to set property. Error [%d]",
+ errProp );
+ User::Leave( errProp ) ;
+ }
+ UpdatePeriodicActionVars();
+ iEventArray.Append( aEvent );
+
+ if ( iState == ECHIdle )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::HandleEventL() iState == ECHIdle => DoHandleEvent" );
+
+ // Process the first new event from the queue
+ TRAPD( err, DoHandleEventsL() );
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleEventL. Assert would fail and Panic IF err != KErrNone. err [%d]", err );
+ RUBY_ASSERT_DEBUG( err == KErrNone, User::Panic( KFile, __LINE__ ) );
+
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::HandleEventL. Error [%d] in DoHandleEventsL", err );
+ }
+ }
+ else
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleEventL() iState == [%d] => Do not handle the event right now", iState );
+ } // if iState else
+ }
+ } // if caused be voice tag addition
+ else
+ {
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::HandleEventL(). Event for Contact id [%d] was caused by a voice icon addition and will be ignored, eventArray size [%d]", aEvent.iContactId, iEventArray.Count() );
+ if ( ( iVoiceTagAddedList.Count() == 0 ) && ( iState == ECHIdle ) && ( iEventArray.Count() == 0 ) )
+ {
+ RUBY_DEBUG0( "HandleEventL No more voice icon events expected, nothing pending." );
+ }
+ else
+ {
+ RUBY_DEBUG1( "HandleEventL [%d] voice icon events are still expected", iVoiceTagAddedList.Count() );
+ }
+ }
+ }
+ else if ( aEvent.iType == EStoreRestoreBeginning )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::HandleEventL restore begin" );
+ iRestoreRunning = ETrue; // set to differentiate ending of restore or backup
+ }
+ else if ( aEvent.iType == EStoreBackupRestoreCompleted && iRestoreRunning )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::HandleEventL restore end" );
+ // Set value to make full resync
+ SetFullResyncCrFlagL( ETrue );
+ iRestoreRunning = EFalse;
+ } // if event type
+ }
+
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoHandleEventL
+// Process the Event from phonebook
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoHandleEventsL()
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::DoHandleEventsL" );
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoHandleEventsL, eventArray size [%d]", iEventArray.Count() );
+
+ // If there is no lengthy higher level routine executed
+ if( !PeriodicAction() )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL !PeriodicAction()" );
+ if ( iHandlerEnabled )
+ {
+ if ( IsFullResyncNeeded() )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL() Full resync if needed. Setting iState to ECHFullResync" );
+ SetState( ECHFullResync );
+
+ iEventArray.Close();
+
+ FullResyncStart();
+ }
+ else
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL() Full resync if NOT needed. Setting iState to ECHHandlingNormalChanges" );
+ SetState( ECHHandlingNormalChanges );
+
+ // There are 3 phases in handling events:
+ // (1) Make two lists:
+ // * Names to be added
+ // * Names to be deleted
+ CollectAddRemoveListsL();
+
+ // (2) Delete the names to be deleted. This operation is asynchronous.
+ DoRemoveNamesL();
+
+ // (3) After removing names, add the names to be added.
+ // DoRemoveNamesL() starts the step (3)
+ }
+ }
+ else
+ {
+ // In UREL, empty "else" branch will be eliminated by the compiler
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL() Handler was disabled" );
+ }
+
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL() completed" );
+
+ }// if !PeriodicAction
+
+ else
+ {
+ // In UREL, empty "else" branch will be eliminated by the compiler
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventsL PeriodicAction detected, DoHandleEventsL omitted" );
+ }
+
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DeleteTagCompleted
+// Called when the deletion of a tag is completed successfully
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DeleteTagCompleted( TInt aErrorCode )
+ {
+ if( aErrorCode == KErrNone )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DeleteTagCompleted. Assert iState == ECHHandlingNormalChanges. iState [%d]", iState);
+ RUBY_ASSERT_DEBUG( iState == ECHHandlingNormalChanges, User::Panic( KFile, __LINE__ ) );
+ }
+ else
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DeleteTagCompleted: tag deletion failed" );
+
+ iNormalChangesError = aErrorCode;
+ }
+
+ DoRemoveNamesAfterDeleteTagCompleted();
+ }
+
+// -----------------------------------------------------------------
+// CNssContactHandlerImplementation::GetTagCompleted
+// Called by tag manager when getting of a tag list completes
+// -----------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::GetTagListCompleted(
+ MNssTagListArray* aTagList, TInt aErrorCode )
+ {
+ if( aErrorCode == KErrNone )
+ {
+ if ( iState == ECHHandlingNormalChanges )
+ {
+ DoRemoveNamesAfterGetTagList( aTagList );
+ }
+ else
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::GetTagListCompleted : Unknown state(%d)\n", iState );
+ }
+ }
+ else
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::GetTagListCompleted. Error [%d]", aErrorCode );
+
+ TRAPD( err, DoHandleEventFailedL() ); // Process the event fail
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::GetTagFailed. Error [%d] in DoHandleEventFailedL", err );
+ }
+ }
+ }
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DisableEventHandling
+// Disables the event handling. Is used in case of unrecoverable error
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DisableEventHandling()
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DisableEventHandling" );
+ iHandlerEnabled = EFalse;
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::EnableEventHandling
+// Enables event handling
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::EnableEventHandling()
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::EnableEventHandling" );
+ iHandlerEnabled = ETrue;
+ }
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContextListCompleted
+// Called by tag manager when GetContextList() completes successfully
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::GetContextListCompleted(
+ MNssContextListArray* /*aContextList*/, TInt /*aErrorCode*/ )
+ {
+ RUBY_ERROR0( "CNssContactHandlerImplementation::GetContextListCompleted - this function should never be called." );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::CheckErrorSigns
+// Compares client data (saved in VAS context)
+// to state of the phonebook to find out if
+// full synchronization is needed.
+//
+// If full resync is needed initiates it by saving into the event queue
+// special 'full resync event'
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::CheckErrorSigns()
+ {
+
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::CheckErrorSigns" );
+ TBool errorFound( EFalse );
+ // Full resync, if this is the first time we launch.
+ // if the UI language has changed.
+ // if VAS Db and Contact Db time stamps do not match
+ if ( iClientData.iLanguage == ELangNone ||
+ iClientData.iLanguage != User::Language() )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::CheckErrorSigns language changed." );
+ errorFound = ETrue;
+ }
+ else
+ {
+ // check that is full resyncronization needed
+ iRepository->Get( KSRSFFullResyncNeeded, errorFound );
+ }
+
+ if ( errorFound )
+ {
+ TPhonebookEvent event;
+ event.iType = ENullEvent;
+ event.iContactId = 0;
+
+ if ( iEventArray.Append( event ) != KErrNone )
+ {
+ RUBY_ERROR0( "CNssContactHandlerImplementation::CheckErrorSigns Failed to append to iEventArray" );
+ }
+ }
+
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContextCompleted
+// Called by tag manager, when GetContext() completes successfully
+//
+// Depending on the contact handler state, continues full resync sequence or
+// sets the idle state
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::GetContextCompleted(
+ MNssContext* aContext, TInt aErrorCode )
+ {
+ if( aErrorCode == KErrNone )
+ {
+ RUBY_ASSERT_DEBUG( iState == ECHInitializing || iState == ECHFullResync,
+ User::Panic( KFile, __LINE__ ) );
+
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::GetContextCompleted" );
+ iContext = aContext;
+ TRAPD(err,
+
+ iClientData.InternalizeL( iContext->ClientData() );
+
+ if ( iState == ECHInitializing )
+ {
+ // If there are warning sings of error,
+ // do full resynchronization.
+ CheckErrorSigns();
+
+ SetState( ECHIdle );
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::GetContextCompleted. Setting iState to ECHIdle" );
+
+ if ( iEventArray.Count() ) // Verify whether more events in the queue
+ {
+
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::GetContextCompletedhere are still [%d] events in the queue. Call DoHandleEvents for them", iEventArray.Count() );
+
+ DoHandleEventsL(); // Process the 1st new event from the queue
+ }
+ }
+ else if ( iState == ECHFullResync )
+ {
+ TRAPD( error, FullResyncCreateChangesL() );
+ if( error != KErrNone )
+ {
+ RUBY_ERROR1( "FullResyncCreateChangesL failed with [%d]. Panic, nothing can be done", error );
+ RUBY_ASSERT_DEBUG( EFalse, User::Panic( KFile, __LINE__ ) );
+ }
+ }
+ else
+ {
+ RUBY_ERROR0( "CNssContactHandlerImplementation::GetContextCompleted Unexpected state" );
+ RUBY_ASSERT_DEBUG( EFalse, User::Panic( KFile, __LINE__ ) );
+ }
+ ); // TRAPD
+ if( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::GetContextCompleted Leaves with [%d] inside", err );
+ }
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::GetContextCompleted completed" );
+ }
+
+ else // aErrorCode != KErrNone
+ {
+ RUBY_ASSERT_DEBUG( iState == ECHInitializing || iState == ECHFullResync,
+ User::Panic( KFile, __LINE__ ) );
+
+ if ( iState == ECHInitializing )
+ {
+ iHaveWeEverSynced = EFalse;
+ SetState ( ECHFullResync );
+ FullResyncStart();
+ }
+ }
+
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::SaveContextCompleted
+// Called when SaveContext() completes successfully
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::SaveContextCompleted( TInt aErrorCode )
+ {
+ if( aErrorCode == KErrNone )
+ {
+ RUBY_ASSERT_DEBUG( iState == ECHInitializing ||
+ iState == ECHFullResync,
+ User::Panic( KFile, __LINE__ ) );
+
+ iHaveWeEverSynced = ETrue;
+
+ if ( iState == ECHInitializing )
+ {
+ // Retrive the Context to get the new ContextId from VasDB
+ iContextManager->GetContext( this, KNssCHNameDialContext );
+ }
+ else if ( iState == ECHFullResync )
+ {
+ TRAPD( error, FullResyncCreateChangesL() );
+ if ( error != KErrNone )
+ {
+ RUBY_ERROR1( "SaveContextCompleted Failed to create full resync changes. Critical error [%d]",
+ error );
+ DisableEventHandling();
+ } // if error != KErrNone
+ } // else if ( iState == ECHFullResync )
+ else
+ {
+ // we should never be here, because the condition is checked
+ // by ASSERT_DEBUG at the start of the function
+ RUBY_ERROR0( "CNssContactHandlerImplementation::SaveContextCompleted Unexpected state!" );
+ }
+ }
+
+ else // aErrorCode != KErrNone
+ {
+ // Failed to initialize will collect the events forever and will never process them
+ // Will try to initialize again after reboot
+ RUBY_ERROR0( "CNssContactHandlerImplementation::SaveContextCompleted failed. Staying forever in ECHFullResync" );
+ RUBY_ERROR0( "CNssContactHandlerImplementation::SaveContextCompleted failed. Reboot to try initializing again" );
+ DisableEventHandling();
+ }
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::SaveTagCompleted
+// Called when SaveTag() completes
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::SaveTagCompleted( TInt aErrorCode )
+ {
+ RUBY_ASSERT_DEBUG( iState == ECHHandlingNormalChanges, User::Panic( KFile, __LINE__ ) );
+
+ DoAddNamesSaveTagCompleted( aErrorCode );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContactTitleL
+// Populates aTitle with either "FirstName LastName" or "CompanyName".
+// "CompanyName" is given only if both First and Last names are missing.
+//---------------------------------------------------------
+//
+HBufC* CNssContactHandlerImplementation::GetContactTitleL( TContactItemId aContactId,
+ TBool aUseDefaultOrder,
+ TBool& aBothFirstAndLastFound,
+ TVasTagType& aTagType )
+ {
+ RUBY_DEBUG_BLOCKL( "CNssContactHandlerImplementation::GetContactTitleL" );
+ // Find out if lastname-firstname should be used
+ TBool useLastFirstOrder = EFalse;
+
+ // By default, this function returns firstname and/or lastname
+ aTagType = ETagTypeName;
+
+ if ( aUseDefaultOrder )
+ {
+ useLastFirstOrder = SwapNameOrder();
+ }
+ else
+ {
+ useLastFirstOrder = !SwapNameOrder();
+ }
+ WaitUntilNoSyncBackupIsRunning();
+ ReadContactL( aContactId );
+
+ HBufC* firstNameText = NULL;
+ HBufC* lastNameText = NULL;
+ HBufC* companyNameText = NULL;
+
+ TInt firstLength( 0 );
+ TInt lastLength( 0 );
+ TInt companyLength( 0 );
+
+ TInt error = KErrNone;
+
+ // name reading fields are used in Japanese
+ TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdFirstNameReading ) );
+ if ( error == KErrNotFound )
+ {
+ // not found
+ TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdFirstName ) );
+ }
+
+ if ( error == KErrNone )
+ {
+ firstNameText = iPbkHandler->TextL().AllocLC();
+ TrimName( firstNameText );
+ firstLength = firstNameText->Length();
+ if ( firstLength == 0 )
+ {
+ CleanupStack::PopAndDestroy( firstNameText );
+ firstNameText = NULL;
+ }
+ }
+
+ TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdLastNameReading ) );
+ if ( error == KErrNotFound )
+ {
+ // not found
+ TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdLastName ) );
+ }
+
+ if ( error == KErrNone )
+ {
+ lastNameText = iPbkHandler->TextL().AllocLC();
+ TrimName( lastNameText );
+ lastLength = lastNameText->Length();
+ if ( lastLength == 0 )
+ {
+ CleanupStack::PopAndDestroy( lastNameText );
+ lastNameText = NULL;
+ }
+ }
+
+ TRAP( error, iPbkHandler->FindFieldL( EPbkFieldIdCompanyName ) );
+
+ if ( error == KErrNone )
+ {
+ companyNameText = iPbkHandler->TextL().AllocLC();
+ TrimName( companyNameText );
+ companyLength = companyNameText->Length();
+ if ( companyLength == 0 )
+ {
+ CleanupStack::PopAndDestroy( companyNameText );
+ companyNameText = NULL;
+ }
+ }
+
+ // Use third parameter to return a flag if both lastname and firstname are found
+ if ( ( firstLength > 0 ) && ( lastLength > 0 ) )
+ {
+ aBothFirstAndLastFound = ETrue;
+ }
+ else
+ {
+ aBothFirstAndLastFound = EFalse;
+ }
+
+ HBufC* title( NULL );
+
+#ifdef __SIND_EXTENSIONS
+ if ( firstLength + lastLength > 0 )
+ {
+ TInt spaceForIndexes( 0 );
+ // Find out how much more space is needed for "training indexes"
+ if ( aBothFirstAndLastFound )
+ {
+ spaceForIndexes = 2 * KTrainingIndexSize;
+ }
+ else
+ {
+ spaceForIndexes = KTrainingIndexSize;
+ }
+
+ title = HBufC::NewL( firstLength + lastLength + spaceForIndexes );
+
+ // Check the order
+ if ( useLastFirstOrder )
+ {
+ // Lastname-firstname
+ if ( lastNameText )
+ {
+ AppendName( title, lastNameText );
+ }
+
+ if ( firstNameText )
+ {
+ AppendName( title, firstNameText );
+ }
+ }
+ else
+ {
+ // Firstname-lastname
+ if ( firstNameText )
+ {
+ AppendName( title, firstNameText );
+ }
+
+ if ( lastNameText )
+ {
+ AppendName( title, lastNameText );
+ }
+ }
+ }
+ // Try company name, if the personal name fails.
+ else if ( companyLength > 0 )
+ {
+ aTagType = ETagTypeCompanyName;
+ title = HBufC::NewL( companyLength + KTrainingIndexSize );
+ AppendName( title, companyNameText );
+ }
+#else
+ // Use personal name, if there is one.
+ if ( firstLength + lastLength > 0 )
+ {
+ title = HBufC::NewL( firstLength + 1 + lastLength );
+
+ // Check the order
+ if ( useLastFirstOrder )
+ {
+ // Lastname-firstname
+ if ( lastNameText )
+ {
+ title->Des().Append( lastNameText->Des() );
+
+ if ( firstNameText )
+ {
+ // Separator marker
+ title->Des().Append( iSeparator );
+ }
+ }
+
+ if ( firstNameText )
+ {
+ title->Des().Append( firstNameText->Des() );
+ }
+
+
+ }
+ else
+ {
+ // Firstname-lastname
+ if ( firstNameText )
+ {
+ title->Des().Append( firstNameText->Des() );
+
+ if ( lastNameText )
+ {
+ // Separator marker
+ title->Des().Append( iSeparator );
+ }
+ }
+
+ if ( lastNameText )
+ {
+ title->Des().Append( lastNameText->Des() );
+ }
+
+ }
+ }
+ // Try company name, if the personal name fails.
+ else if ( companyLength > 0 )
+ {
+ aTagType = ETagTypeCompanyName;
+ title = HBufC::NewL( companyLength );
+ title->Des().Append( companyNameText->Des() );
+ }
+#endif // __SIND_EXTENSIONS
+ // It is possible to make a contact without a name.
+ else
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::ContactTitle: No name, ContactId=[%d]" , aContactId );
+ }
+
+ if ( companyNameText )
+ {
+ CleanupStack::PopAndDestroy( companyNameText );
+ }
+
+ if ( lastNameText )
+ {
+ CleanupStack::PopAndDestroy( lastNameText );
+ }
+
+ if ( firstNameText )
+ {
+ CleanupStack::PopAndDestroy( firstNameText );
+ }
+
+ return title;
+ }
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::SwapNameOrder
+// Returns ETrue if name order for current UI language should
+// be lastname-firstname, EFalse otherwise
+//---------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::SwapNameOrder()
+ {
+ // Find UI language
+ TLanguage uiLanguage = User::Language();
+
+ if ( iSwappedLanguages.Find( uiLanguage ) == KErrNotFound )
+ {
+ return EFalse;
+ }
+ else
+ {
+ return ETrue;
+ }
+ }
+
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContactNicknameL
+// Returns nickname for a given contact
+//---------------------------------------------------------
+//
+HBufC* CNssContactHandlerImplementation::GetContactNicknameL( TContactItemId aContactId )
+ {
+ RUBY_DEBUG_BLOCKL( "CNssContactHandlerImplementation::GetContactNicknameL" );
+ WaitUntilNoSyncBackupIsRunning();
+ ReadContactL( aContactId );
+
+ // Get the nick name
+ iPbkHandler->FindFieldL( EPbkFieldIdSecondName );
+
+ // Get the length of the additional name.
+ TInt nickLength = iPbkHandler->TextL().Length();
+
+ HBufC* title = NULL;
+
+#ifdef __SIND_EXTENSIONS
+ // Use personal name, if there is one.
+ if ( nickLength > 0 )
+ {
+ title = HBufC::NewL( nickLength + KTrainingIndexSize );
+
+ HBufC* nickName = iPbkHandler->TextL().AllocL();
+ TrimName( nickName );
+ AppendName( title, nickName );
+ delete nickName;
+ }
+#else
+ // Use personal name, if there is one.
+ if ( nickLength > 0 )
+ {
+ title = HBufC::NewL( nickLength );
+ title->Des().Append( iPbkHandler->TextL() );
+ }
+#endif // __SIND_EXTENSIONS
+
+ return title;
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoHandleEventFailed
+// Routine to process when handling the contact event failed
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoHandleEventFailedL()
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventFailedL start" );
+
+ // Errors are normal if user deletes contact without a voice tag
+ // E.g. because this contact had no number
+ if( ( iDeleteTagListArray != NULL ) && ( iDeleteTagListArray->Count() > 0 ) )
+ {
+ // simulate successful deletion, just return no tags for further deletions
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventFailedL Deletion failed. Simulating successful empty deletion" );
+ iDeletionSimulator->Call();
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventFailedL Called deletion simulator" );
+ }
+ else
+ {
+
+ // Try full resync couple of times. If still fails, stop contact handler.
+ // When the phone is booted for the next time, try resyncing again
+ if( iConsecutiveErrorFullResync < KMaxConsecutiveErrorFullResync )
+ {
+ iConsecutiveErrorFullResync++;
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoHandleEventFailedL Trying full resync for the [%d] time", iConsecutiveErrorFullResync);
+ // clean structures that might be already full
+ iTagArray.ResetAndDestroy();
+
+ for( TInt k( 0 ); k < iContactQueue.Count(); k++ )
+ {
+ delete iContactQueue[k].iTitle;
+ }
+ iContactQueue.Close();
+
+ iDelList.Close();
+ iAddList.Close();
+ iVoiceTagAddedList.Close();
+ // generate full resync event
+ TPhonebookEvent event;
+ event.iType = ENullEvent;
+ event.iContactId = 0;
+ iEventArray.Insert( event, 0 );
+ // will start full resync
+ iTagArray.ResetAndDestroy();
+ iSaveCallbackCounter = 0; // simulate completed action
+ RUBY_DEBUG0( "DoHandleEventFailedL No leaving in the FullResync branch, retrying" );
+ DoHandleEventsL(); // No leaving in the FullResync branch
+ }
+ else
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DoHandleEventFailedL Tried full resync for %d times. Disabling contact handler until reboot", KMaxConsecutiveErrorFullResync );
+ // Tried several consecutive resyncs, didn't help
+ // Disable itself until reboot
+ DisableEventHandling();
+ }
+ } // if deletion else
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoHandleEventFailedL end" );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::PhonebookOrder
+// TLinearOrder comparison function for sorting phonebook events
+// according to their Phonebook ID.
+//---------------------------------------------------------
+//
+TInt CNssContactHandlerImplementation::PhonebookOrder( const TPhonebookEvent& a1, const TPhonebookEvent& a2 )
+ {
+ if ( a1.iContactId != a2.iContactId )
+ {
+ return( a1.iContactId - a2.iContactId );
+ }
+
+ if ( a1.iTime < a2.iTime )
+ {
+ return( -1 );
+ }
+ else if ( a1.iTime > a2.iTime )
+ {
+ return( 1 );
+ }
+ else{
+ return( 0 );
+ }
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::IsFullResyncNeeded
+// Checks if the event queue contains an event, which
+// launches full resynchronization. If it does, there is
+// no point in processing the other events.
+// ---------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::IsFullResyncNeeded()
+ {
+ for( TInt k( 0 ); k < iEventArray.Count(); k++ )
+ {
+ // Null event is used inside contact handler to signal full resync.
+ if ( iEventArray[k].iType == ENullEvent
+ || iEventArray[k].iType == EStoreBackupRestoreCompleted
+ || iEventArray[k].iType == EUnknownChanges )
+ {
+ return ETrue;
+ }
+ }
+
+ return EFalse;
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::CollectAddRemoveListsL
+// Given a list of Contact DB Events, this function compiles
+// a list of ids to-be-deleted and ids to-be-added.
+// A Contact DB Event contains:
+// * Contact ID
+// * Event type (added, modified, removed)
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::CollectAddRemoveListsL()
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::CollectAddRemoveListsL" );
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL(). iEventArray.Count [%d]", iEventArray.Count() );
+ TInt k( 0 );
+
+ RUBY_ASSERT_DEBUG( iAddList.Count() == 0, User::Panic( KFile, __LINE__ ) );
+ RUBY_ASSERT_DEBUG( iDelList.Count() == 0, User::Panic( KFile, __LINE__ ) );
+
+ // We collect two lists:
+ // * Contacts to be deleted.
+ // * Contacts to be added.
+
+ TLinearOrder<TPhonebookEvent> order( PhonebookOrder );
+
+ // Sort events according to the phonebook ID. This way, the events
+ // modifying the same name are sequentially.
+ iEventArray.Sort( order );
+
+
+ // If several events happen for the same contact, fuse them into one event.
+ for ( k = 0; k < iEventArray.Count() - 1; k++ )
+ {
+ if ( iEventArray[k].iContactId == iEventArray[k+1].iContactId )
+ {
+ // Sorting should have preserved the order of the events
+ RUBY_ASSERT_DEBUG(iEventArray[k+1].iTime >= iEventArray[k].iTime, User::Panic( KFile, __LINE__ ) );
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL(). Fusing events for ContactId [%d]", iEventArray[k].iContactId );
+ iEventArray[k+1].iType = EContactChanged;
+ iEventArray.Remove( k );
+
+ // We have removed an element
+ k--;
+ }
+ }
+
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL(). Events sorted and fused iEventArray.Count [%d]", iEventArray.Count() );
+
+ for( k = 0; k < iEventArray.Count() && k < KMaxNamesAtOnce; k++ )
+ {
+ TPhonebookEvent* event = &iEventArray[k];
+
+ switch ( event->iType )
+ {
+ case EContactChanged:
+ {
+ // events caused by saving voice tag field are skipped
+ // earlier, in HandleDatabaseEventL
+
+ // We need to delete the old name
+ User::LeaveIfError( iDelList.Append( *event ) );
+ // and after that, add the new name.
+ User::LeaveIfError( iAddList.Append( *event ) );
+ break;
+ }
+ case EContactDeleted:
+ {
+ User::LeaveIfError( iDelList.Append( *event ) );
+ break;
+ }
+ case EContactAdded:
+ {
+ User::LeaveIfError( iAddList.Append( *event ) );
+ break;
+ }
+ default:
+ RUBY_ERROR1( "CNssContactHandlerImplementation::CollectAddRemoveListsL() Unkown event type: %d", event->iType );
+ break;
+ }
+ }
+
+ if ( k < KMaxNamesAtOnce )
+ {
+ iEventArray.Close();
+ }
+ else
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL(). k >= KMaxNamesAtOnce [%d]. Deleting first KMaxNamesAtOnce events, they've been sorted into corresponding lists already",
+ KMaxNamesAtOnce);
+ for ( k = 0; k < KMaxNamesAtOnce; k++ )
+ {
+ iEventArray.Remove(0);
+ }
+ }
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::CollectAddRemoveListsL() iEventArray.Count [%d]",
+ iEventArray.Count());
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::CollectAddRemoveListsL iDelList.Count() [%d], iAddList.Count() [%d]", iDelList.Count(), iAddList.Count());
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoRemoveNamesL
+// Starts name removal sequence
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoRemoveNamesL()
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::DoRemoveNamesL" );
+ if ( iDelList.Count() == 0 )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL Nothing to delete - skipping" );
+
+ RemovingNamesCompletedL();
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL completed early <= nothing to remove" );
+ return;
+ }
+
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL - removing" );
+
+ // we have: a list of tag IDs, which should be removed
+ // we need: a list of tags, which should be removed.
+
+ // First, we get the tags which correspond to the tag ids.
+ // We need k async calls to VAS,
+ // where k is the number of tag ids in iDelList.
+ // We get a list of k' tags, where k' >= k.
+ // If some tag has both proper name and a nickname, then k' > k.
+
+ iContactId = iDelList[0].iContactId;
+
+ if ( iDeleteTagListArray )
+ {
+ iDeleteTagListArray->ResetAndDestroy();
+ }
+ else
+ {
+ iDeleteTagListArray = new (ELeave) CArrayPtrFlat<MNssTag>(50);
+ }
+
+ TInt retCode = iTagManager->GetTagList( this, iContext, iContactId, 0 );
+
+ if ( retCode != KErrNone )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL failed to GetTagList" );
+ // In the case of change event, try to add names even though they are not found
+ // Might happen in scenario like this:
+ // 1. User adds a name without any phone number -> no voice tag is created
+ // 2. Contact is modified by adding a phone number
+ iDelList.Close();
+ DoAddNamesL();
+ }
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesL completed" );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList
+// If contact database announces that n contacts need to be
+// deleted, we do n calls to GetTagList (the first is from DoRemoveNamesL).
+// Each callback returns 1 or more tags (name + nickname = 2 tags).
+// This function processes the callbacks by adding the tags to the
+// to-be-deleted list.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList( MNssTagListArray* aTagList )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList. iState [%d]", iState );
+
+ if ( iDelList.Count() == 0 )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList iDelList.Count() == 0" );
+ }
+
+ // Sieve out "false" tags: Those which have a wrong contact ID.
+ /** Why? Maybe user was fast enough to delete these contacts from the phonebook
+ * Anyway, why should we keep "false" tags in our db?
+ */
+ for ( TInt i=0; i < aTagList->Count(); i++ )
+ {
+ CArrayFixFlat<TInt>* intArray = aTagList->At(i)->RRD()->IntArray();
+
+ RUBY_ASSERT_DEBUG( intArray->Count() > 0, User::Panic( KFile, __LINE__ ) );
+
+ if ( intArray->At(0) != iContactId )
+ {
+ RUBY_ERROR2( "DoRemoveNamesAfterGetTagList expected contact id %d, but got contact id: %d", iContactId, intArray->At(0) );
+ delete aTagList->At( i );
+ aTagList->Delete( i );
+ }
+ }
+
+ TRAPD( err, // trapping is a fix to make the function non-leaving
+
+ // Add the tags to the to-be-deleted list.
+ // (there are 1 or 2 tags in aTagList depending on
+ // whether nickname is also there or not)
+ for ( TInt k( 0 ); k < aTagList->Count(); k++ )
+ {
+ iDeleteTagListArray->AppendL( (*aTagList)[k] );
+ }
+
+ // We have processed this ID
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList Before removal. iDelList.Count [%d]", iDelList.Count() );
+ if( iDelList.Count() > 0 )
+ {
+ iDelList.Remove( 0 );
+ }
+ else
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList Empty iDelList!" );
+ }
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList After removal. iDelList.Count [%d]", iDelList.Count() );
+
+ // If there are still more IDs, convert the next ID into a tag.
+ if ( iDelList.Count() > 0 )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList There are [%d] contacts to delete. Calling GetTagList for them", iDelList.Count() );
+ iContactId = iDelList[0].iContactId;
+
+ TInt retCode
+ = iTagManager->GetTagList( this, iContext, iContactId, 0 );
+
+ if ( retCode != KErrNone )
+ {
+ RUBY_DEBUG0( "DoRemoveNamesAfterGetTagList Failed to GetTagList, going to DoHandleEventFailedL" );
+ TRAP_IGNORE( DoHandleEventFailedL() ); // to handle the process event failed
+ /** @todo reengineer into single return path */
+ // Destroy tag list
+ aTagList->Reset();
+ delete aTagList;
+ aTagList = 0;
+ return;
+ }
+ }
+
+ // If we just finished converting the last ID, move to the next phase.
+ else{
+ RUBY_DEBUG0( "DoRemoveNamesAfterGetTagList Moving to the next phase" );
+ DoRemoveNamesCallDeleteTag();
+ }
+ ); // TRAPD
+ if( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DoRemoveNamesAfterGetTagList Leaves with [%d] inside", err );
+ }
+ // Destroy tag list
+ aTagList->Reset();
+ delete aTagList;
+ aTagList = 0;
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag
+// Sends the DeleteTag() calls to lower layers when removing names.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag()
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag. iDeleteTagListArray->Count() [%d] Setting iDeleteCallbackCounter = 0",
+ iDeleteTagListArray->Count() );
+
+ if( iDeleteTagListArray->Count() > 0 )
+ {
+
+ iDeleteCallbackCounter = 0;
+
+ for ( TInt k( 0 ); k < iDeleteTagListArray->Count(); k++ )
+ {
+ TInt ret =
+ iTagManager->DeleteTag( this, (*iDeleteTagListArray)[k] );
+
+ if ( ret != KErrNone )
+ {
+ TRAPD( err, DoHandleEventFailedL(); );
+ if( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag Error handling Left with [%d]", err );
+ }
+ return; /** @todo reengineer into single return path */
+ }
+ }
+ } // if there are tags to delete
+ else
+ {
+ // if there were no tags to delete (attempt to delete non-existing tags)
+ // simulating callback from successful deletion of.. 0 tags
+ //iDeleteCallbackCounter = -1;
+ //DeleteTagCompleted();
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesCallDeleteTag Here!" );
+ }
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted
+// DeleteTag() callback calls this funciton. It counts
+// the number of callbacks, and when all callbacks have arrived,
+// goes to the next phase.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted()
+ {
+ iDeleteCallbackCounter++;
+
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted. iDeleteCallbackCounter [%d], Count() [%d]", iDeleteCallbackCounter, iDeleteTagListArray->Count() );
+
+ if ( iDeleteCallbackCounter == iDeleteTagListArray->Count() )
+ {
+ if( iNormalChangesError == KErrNone )
+ {
+ TRAPD( err, RemovingNamesCompletedL() );
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted. Error [%d] in RemovingNamesCompletedL", err );
+ }
+ }
+ else
+ {
+ // There is not much we can do about the deletion error
+ // Retrying deletion can cause endless cycle
+ // Sometimes failed deletions are ok (if we tried to delete non-existing tag)
+ /** @todo reengineer SpeechItemTrainer (and/or SRS plugin) to report deletion
+ of non-existing tag as success. After all they are not in the DB */
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted Deletion failed. Still simulating a successful one" );
+ TRAPD( err, RemovingNamesCompletedL() );
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DoRemoveNamesAfterDeleteTagCompleted. Error [%d] in RemovingNamesCompletedL", err );
+ }
+ } // if (iNormalChangesError == KErrNone) else
+ } // if count
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::RemovingNamesCompletedL
+// Cleans up after some names have been removed
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::RemovingNamesCompletedL()
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::RemovingNamesCompletedL" );
+ iDelList.Close();
+
+ if ( iDeleteTagListArray )
+ {
+ iDeleteTagListArray->ResetAndDestroy();
+ iDeleteCallbackCounter = 0;
+ }
+
+ DoAddNamesL();
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesL
+// Starts name addition sequence by fetching contacts data and ordering
+// voice tags training
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesL()
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::DoAddNamesL" );
+ TInt k( 0 );
+
+ if ( iAddList.Count() == 0 )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesL Nothing to add. Setting iState = ECHIdle. Return" );
+ SetState ( ECHIdle );
+ // If there are events on the list, DoAddNamesCompleted will fire them
+ DoAddNamesCompletedL();
+ }
+ else
+ {
+ // Create new tags and train them.
+ for ( k = 0; k < iAddList.Count(); k++ )
+ {
+ // 3 names may be created from one contact:
+ // * Main name (firstname + lastname XOR companyname)
+ // * Reversed order name (usually lastname + firstname, depends on the UI language)
+ // -> only if NSSVAS_CONTACTHANDLER_FREEORDER is defined
+ // * Nickname
+
+ TContactData contactData;
+
+ contactData.iID = iAddList[k].iContactId;
+ contactData.iSyncTime.UniversalTime();
+
+ // Nickname
+ contactData.iTitle = NULL;
+ TRAPD( err, contactData.iTitle = GetContactNicknameL( iAddList[k].iContactId ) );
+
+ if ( contactData.iTitle != NULL )
+ {
+ CleanupStack::PushL( contactData.iTitle );
+ contactData.iType = ETagTypeNickName;
+ User::LeaveIfError( iContactQueue.Append( contactData ) );
+ CleanupStack::Pop( contactData.iTitle );
+ }
+
+ // Main name ( Using GetContactTitleL() )
+ contactData.iTitle = NULL;
+ TBool bothFound = EFalse;
+ TVasTagType tagType( ETagTypeUnknown );
+ TRAP( err, contactData.iTitle = GetContactTitleL( iAddList[k].iContactId, ETrue, bothFound, tagType ) );
+
+ // If all the name fields are empty, nothing
+ // can be done for the tag. We're finished.
+ if ( contactData.iTitle != NULL )
+ {
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesL For Contact id [%d] title is: [%S]", iAddList[k].iContactId, contactData.iTitle );
+ CleanupStack::PushL( contactData.iTitle );
+ contactData.iType = tagType;
+ User::LeaveIfError( iContactQueue.Append( contactData ) );
+ CleanupStack::Pop( contactData.iTitle );
+ }
+
+ #ifdef NSSVAS_CONTACTHANDLER_FREEORDER
+ // Default order added previously, now adding
+ // secondary order if both first and lastnames are found
+ if ( bothFound )
+ {
+ contactData.iTitle = NULL;
+ TRAPD( err, contactData.iTitle = GetContactTitleL( iAddList[k].iContactId, EFalse, bothFound, tagType ) );
+ if ( contactData.iTitle != NULL )
+ {
+ CleanupStack::PushL( contactData.iTitle );
+ contactData.iType = tagType;
+ User::LeaveIfError( iContactQueue.Append( contactData ) );
+ CleanupStack::Pop( contactData.iTitle );
+ }
+ }
+ #endif // NSSVAS_CONTACTHANDLER_FREEORDER
+
+ }
+
+ // No tag had any text.
+ if ( iContactQueue.Count() == 0 )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesL. No tag had any text, return" );
+ DoAddNamesCompletedL();
+ }
+ else
+ {
+ // At least some tag has some text
+ RUBY_ASSERT_DEBUG( iTagArray.Count() == 0, User::Panic( KFile, __LINE__ ) );
+
+ for ( k = 0; k < iContactQueue.Count(); k++ )
+ {
+ // (1) Create a new tag
+ if ( iNewTag != NULL )
+ {
+ // If previous call to this function leaves, iNewTag can
+ // be not deleted
+ delete iNewTag;
+ iNewTag = NULL;
+ }
+ iNewTag = iTagManager->CreateTagL( iContext );
+
+ MNssRRD* rrd = iNewTag->RRD();
+ MNssSpeechItem* speechItem = iNewTag->SpeechItem();
+
+ // (2) Fill RRD. RRD contains client-chosen data about the tag.
+ // In case of name dialing, RRD contains the contact ID,
+ // which is used later to get the phone number.
+ CArrayFixFlat<TInt> *id = new (ELeave) CArrayFixFlat<TInt>( 1 );
+
+ CleanupStack::PushL( id );
+
+ id->AppendL( iContactQueue[k].iID ); // ID
+ // select a number field for voice tag
+ // TInt fieldIndex( 0 );
+
+ WaitUntilNoSyncBackupIsRunning();
+ TRAPD( err, ReadContactL( iContactQueue[k].iID ); );
+ if ( err == KErrNone )
+ {
+ // 1. Default voice call number set by the user
+ TInt found = KErrNotFound;
+ TRAP( found, iPbkHandler->FindDefaultContactFieldL( EDefaultCommand ) );
+
+ if ( found == KErrNotFound )
+ {
+ for( TInt i = 0; i < KTaggableFieldsCount; i++)
+ {
+ TRAP( found, iPbkHandler->FindFieldL( KTaggableFields[i] ) );
+ if( found == KErrNone && ( !iPbkHandler->IsFieldEmptyL() ) )
+ {
+ break; // found field
+ }
+ else
+ {
+ // When field->IsEmptyOrAllSpaces(), it is the same for us as no field present
+ found = KErrNotFound;
+ }
+ } // for i < KTaggableFieldsCount
+ }
+
+ if ( found == KErrNone ) // found and is not empty
+ {
+ id->AppendL( iPbkHandler->FieldIdL() ); // field ID to array
+ id->AppendL( EDial );
+ id->AppendL( iContactQueue[k].iType );
+ id->AppendL( EDefaultCommand );
+ rrd->SetIntArrayL ( id );
+
+ // (3) Set the training text.
+ speechItem = iNewTag->SpeechItem();
+ speechItem->SetTextL( *iContactQueue[k].iTitle );
+
+ User::LeaveIfError( iTagArray.Append( iNewTag ) );
+ // Leave protection for iNewTag completed
+ // Ownership and deletion responsibility moved
+ // to iTagArray
+ iNewTag = NULL;
+ }
+ else
+ {
+ // field not found or is empty
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesL Did NOT identify a suitable voice dial field for contact id [%d]", iContactQueue[k].iID);
+
+
+ delete iNewTag;
+ iNewTag = NULL;
+ } // if field else
+#ifdef __SIND_EXTENSIONS
+
+ // this part creates separate voice tags for extensions
+ for ( TInt i(0); i < iExtensionList.Count(); i++ )
+ {
+ found = KErrNotFound;
+ TVasExtensionAction action = iExtensionList[i].iAction;
+ TPbkFieldId fieldId = iExtensionList[i].iId;
+
+ // default message number
+ // default video
+ // default e-mail
+ // default VOIP
+ if ( action == ENewMessage ||
+ iExtensionList[i].iId == EPbkFieldIdPhoneNumberVideo ||
+ iExtensionList[i].iId == EPbkFieldIdEmailAddress ||
+ iExtensionList[i].iId == EPbkFieldIdVOIP )
+ {
+ // @todo Should use DefaultMessageField or something similar!!
+ TRAP( found,
+ iPbkHandler->FindDefaultContactFieldL( iExtensionList[i].iCommand ) );
+ }
+
+ /*if ( extensionField == NULL )
+ {
+ extensionField = item->FindField( iExtensionList[i].iId );
+ }
+
+ if ( extensionField == NULL && field != NULL
+ && ( action == ENewMessage ) )
+ {
+ // if default neither default field or mobile field was not found
+ // message use normal SIND field
+ extensionField = field;
+ fieldId = field->FieldInfo().FieldId();
+ }*/
+
+ // If field was not found, try to find it based on the priority lists
+ if ( found == KErrNotFound )
+ {
+ found = FindExtensionField( action, iExtensionList[i].iId );
+ // If field was found, check the field ID
+ if ( found == KErrNone )
+ {
+ fieldId = iPbkHandler->FieldIdL();
+ }
+ }
+
+ if ( found == KErrNone )
+ {
+ // create new tag
+ iNewTag = iTagManager->CreateTagL( iContext );
+
+ rrd = iNewTag->RRD();
+ speechItem = iNewTag->SpeechItem();
+
+ // set RRD data
+ CArrayFixFlat<TInt> *extIds = new (ELeave) CArrayFixFlat<TInt>( 1 );
+ CleanupStack::PushL( extIds );
+
+ extIds->AppendL( iContactQueue[k].iID );
+ extIds->AppendL( fieldId );
+ extIds->AppendL( action );
+ extIds->AppendL( iContactQueue[k].iType );
+ extIds->AppendL( iExtensionList[i].iCommand );
+
+ rrd->SetIntArrayL ( extIds );
+
+ // set the training text.
+ TPtr namePtr( iContactQueue[k].iTitle->Des() );
+ TPtr extensionPtr( iExtensionList[i].iText->Des() );
+ HBufC* tempTitle = HBufC::NewLC( namePtr.Length() + extensionPtr.Length() + KTrainingIndexSize );
+ TPtr titlePtr( tempTitle->Des() );
+
+ //not work: titlePtr.AppendFormat( extensionPtr, namePtr );
+
+ titlePtr = extensionPtr;
+ AppendExtensionMarker( titlePtr );
+ TInt index = titlePtr.Find( KExtensionFormatString );
+ if ( index >= 0 )
+ {
+ // replace %U with name
+ titlePtr.Delete( index, 2 ); // delete %U
+ titlePtr.Insert( index, namePtr );
+ }
+ speechItem->SetTextL( titlePtr );
+
+ User::LeaveIfError( iTagArray.Append( iNewTag ) );
+ iNewTag = NULL; //ownership changed
+ CleanupStack::PopAndDestroy( tempTitle );
+ CleanupStack::PopAndDestroy( extIds );
+ }
+ }
+
+#endif // __SIND_EXTENSIONS
+ // delete id
+ CleanupStack::PopAndDestroy( id );
+ }
+ else
+ {
+ // Opening contact failed. E.g. if it was just deleted by user
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesL Failed to open contact id [%d]. Leave [%d]", iContactQueue[k].iID, err);
+ CleanupStack::PopAndDestroy( id );
+ delete iNewTag;
+ iNewTag = NULL;
+ /** @todo Try reengineering duplicated cleanups into single piece
+ of the code */
+ }
+ } // for k .. iContactQueue
+
+ if ( iTagArray.Count() == 0 )
+ {
+ // If there are events on the list, DoAddNamesCompleted will fire them
+ DoAddNamesCompletedL();
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesL. Returned from DoAddNamesCompleted. Setting iState = ECHIdle" );
+ SetState ( ECHIdle );
+ }
+ else
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesL Before training tags. TagCount = [%d]",
+ iTagArray.Count() );
+
+ iTrainCallbackCounter = 0;
+ for ( k = 0; k < iTagArray.Count(); k++ )
+ {
+ MNssSpeechItem* speechItem = iTagArray[k]->SpeechItem();
+
+ MNssSpeechItem::TNssSpeechItemResult trainRet
+ = speechItem->TrainTextL( this, iTrainingParams );
+
+ if ( trainRet != MNssSpeechItem::EVasErrorNone )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesL : TrainTextL call failed." );
+ // Not much we can do except for timed retry.
+ // This stops the contact handler.
+ DoHandleEventFailedL();
+ break; // no processing anymore. Quit function
+ }
+ } // for k < iTagArray.Count()
+ } // if iTagArrayCount != 0
+ }
+ } // if ( iAddList.Count() != 0 )
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesTrainingFinished
+// Called after training of a current pack of tags has finished.
+// Starts saving tags.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesTrainingFinished()
+ {
+ iSaveCallbackCounter = 0;
+ iRetry = 0; // retry, if save tag fails
+ iNormalChangesError = 0; // to record failures in save tag
+
+ DoAddNamesSaveTag();
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesSaveTag
+// This function has two branches:
+// (1) Initiate saving tags
+// (1) After all tags have been saved, go to the next phase.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesSaveTag()
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag" );
+ TInt retCode;
+
+ // If all tags have been saved, go to the next phase.
+ if ( iSaveCallbackCounter == iTagArray.Count() )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag All tags have been saved, go to the next phase" );
+ // Tags saved - go to the next phase
+ if ( iNormalChangesError == KErrNone )
+ {
+ // This function is called once per 100 contacts -> It is ok to use TRAP
+ TRAPD( error, DoAddNamesCompletedL() );
+ if( error != KErrNone )
+ {
+ /** @todo Test this branch */
+ // Some fatal error occured
+ RUBY_ERROR1( "DoAddNamesSaveTag DoAddNamesCompletedL failed with [%d]. Disabling SIND",
+ error );
+ DisableEventHandling();
+ }
+ }
+ else
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag Saving tags failed. Try again a few times" );
+ // Saving tags failed. Try again a few times, and if it still
+ // doesn't work, go to fatal error handler.
+ if ( iRetry < KVASCHMaxRetry )
+ {
+ iRetry++;
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesSaveTag Retried saving [%d] times. [%d] retries left",
+ iRetry, KVASCHMaxRetry - iRetry);
+ iSaveCallbackCounter = 0;
+ iNormalChangesError = KErrNone;
+
+ DoAddNamesSaveTag();
+ }
+ else
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesSaveTag Retried saving [%d] times. Still saving fails. Handling error", iRetry );
+ NormalChangesHandleError();
+ }
+ }
+ } // if ( iSaveCallbackCounter == iTagArray.Count() )
+
+ // If this is the first call to this function,
+ // make "save tag" calls. After that, wait for the callbacks.
+ else if ( iSaveCallbackCounter == 0 )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesSaveTag iSaveCallbackCounter == 0. Run SaveTag for [%d] contacts",
+ iTagArray.Count());
+ for ( TInt k( 0 ); k < iTagArray.Count(); k++ )
+ {
+ retCode = iTagManager->SaveTag( this, iTagArray[k] );
+
+ if ( retCode != KErrNone )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag - SaveTag failed." );
+ TRAPD( err, DoHandleEventFailedL(); );
+ if( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DoAddNamesSaveTag handling error Left with [%d]", err );
+ }
+ return;
+ } // if retCode
+ } // for iTagArray
+ } // else if iSaveCallbackCounter == 0
+ else
+ {
+ // iSaveCallbackCounter != 0, but different from iTagArray.Count()
+ // This means, that SaveTag callbacks are still coming. We will wait for them
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesSaveTag. iSaveCallbackCounter != 0, but different from iTagArray.Count()" );
+ }
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesSaveTagCompleted
+// Saving single tag has completed.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesSaveTagCompleted( TInt aError )
+ {
+ iSaveCallbackCounter++;
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesSaveTagCompleted iSaveCallbackCounter became [%d]",
+ iSaveCallbackCounter);
+ // |= to let single KErrGeneral fail any number of KErrNone
+ iNormalChangesError |= aError;
+
+ // Check if all tags have been saved.
+ DoAddNamesSaveTag();
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoAddNamesCompleted
+// When adding names has successfully finished, clean up.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoAddNamesCompletedL()
+ {
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesCompleted. iDeleteCallbackCounter [%d], Count() [%d]", iDeleteCallbackCounter, iDeleteTagListArray->Count() );
+ TInt initialDeleteTagListCount = iDeleteTagListArray->Count();
+
+ iSaveCallbackCounter = 0;
+ iTrainCallbackCounter = 0;
+
+ // DoAddNamesCompleted if often called after voice tags have been added =>
+ // Nothing has to be done at all
+ TBool changedDatabase = EFalse;
+
+#ifndef __SIND_EXTENSIONS
+// Set VAS_SETS_VOICETAG on if old behaviour is needed.
+// That is, VAS sets on the voice tag field to Phonebook engine and Phonebook UI checks the contacts DB for voice tags.
+// Off means that VAS does not set voice tag field but instead Phonebook asks from VAS.
+#ifdef VAS_SETS_VOICETAG
+ // add voice tag fields
+ // iTagArray contains only successfully trained tags.
+ // Failed items are removed.
+ for ( TInt i( 0 ); i < iTagArray.Count(); i++ )
+ {
+ MNssRRD* rrd = iTagArray[i]->RRD();
+ CArrayFixFlat<TInt>* intArray = rrd->IntArray();
+ RUBY_ASSERT_DEBUG( intArray->Count() >= 2, User::Panic( KFile, __LINE__ ) );
+ if ( intArray->Count() >= 2 )
+ {
+ TBool committed( EFalse ); // ETrue if contact is committed already
+ TInt contactId( intArray->At( 0 ) );
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesCompleted. Working on contact id [%d]",
+ contactId);
+ TInt fieldId( intArray->At( 1 ) );
+
+ WaitUntilNoSyncBackupIsRunning();
+ // Open contact item based on id
+ TRAPD( errFor,
+ OpenContactL( contactId );
+
+ RUBY_DEBUG1( "DoAddNamesCompleted Opened contact [%d]", contactId );
+ TInt found == KErrNotFound;
+ TRAP( found, iPbkHandler->FindDefaultContactFieldL( EDefaultCommand ) );
+ if ( found == KErrNotFound )
+ {
+ TRAP( found, iPbkHandler->FindFieldL( fieldId ) );
+ }
+ // field - field, that SHOULD have voice tag attached
+
+ if ( found == KErrNone )
+ {
+ TRAP( error, iPbkHandler->ChangeVoiceTagFieldL( ETrue ) );
+ if ( error == KErrNone )
+ {
+ RUBY_DEBUG1("DoAddNamesCompleted Successfully added icon to the contact [%d]",
+ contactId);
+ iPbkHandler->CloseContactL( ETrue );
+ RUBY_DEBUG1("DoAddNamesCompleted Successfully committed contact [%d]",
+ contactId);
+ iVoiceTagAddedList.Append( contactId );
+ changedDatabase = ETrue;
+ committed = ETrue;
+ }
+ }
+ else
+ {
+ // field not found anymore
+ RUBY_DEBUG2("DoAddNamesCompleted Field [%d] not found in (already deleted from?) contact [%d]",
+ fieldId, contactId );
+ iDeleteTagListArray->AppendL( NULL ); // One DeleteTag will generate one callback
+ iTagManager->DeleteTag( this, iTagArray[i] );
+ }
+ if ( !committed )
+ {
+ RUBY_DEBUG1("DoAddNamesCompleted Did not change contact [%d]. Closing without committing",
+ contactId);
+ iPbkHandler->CloseContactL( EFalse );
+ }
+ ); // TRAPD errFor
+ if ( errFor != KErrNone )
+ {
+ RUBY_DEBUG2("CNssContactHandlerImplementation::DoAddNamesCompleted Failed to add icon for contact [%d]. Error [%d]",
+ contactId, errFor);
+ // voice tag was not in the contacts database, delete it from VAS
+ // One DeleteTag will generate one callback
+ iDeleteTagListArray->AppendL( NULL );
+ iTagManager->DeleteTag( this, iTagArray[i] );
+ }
+ } // if intArray->Count() >= 2
+ } // for iTagArray
+#endif // VAS_SETS_VOICETAG
+#endif //__SIND_EXTENSIONS
+
+ iTagArray.ResetAndDestroy();
+
+ iAddList.Close();
+
+#ifndef __SIND_EXTENSIONS
+#ifdef VAS_SETS_VOICETAG
+ // Remove voice tag icons for contacts, that failed to be trained or saved
+ for( TInt l = 0 ; l < iRemoveIconList.Count(); l++ )
+ {
+ WaitUntilNoSyncBackupIsRunning();
+ TRAPD( error, OpenContactL( iRemoveIconList[l] ) );
+ if ( error != KErrNone )
+ {
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to open contact [%d]. Error [%d]", iRemoveIconList[l], error);
+ }
+ else
+ {
+ TRAP( error, iPbkHandler->ChangeVoiceTagFieldL( EFalse ) );
+ if ( error == KErrNotFound )
+ {
+ // Nothing to remove. Already no voice tag icon
+ TRAP( error, iPbkHandler->CloseContactL( EFalse ) );
+ if ( error != KErrNone )
+ {
+ RUBY_ERROR2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to close contact [%d]. Error [%d]", iRemoveIconList[l], error);
+ }
+ }
+ else if ( error != KErrNone )
+ {
+ RUBY_ERROR2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to remove voice tag icon for contact [%d]. Error [%d]", iRemoveIconList[l], error);
+ TRAP( error, iPbkHandler->CloseContactL( EFalse ) );
+ if ( error != KErrNone )
+ {
+ RUBY_ERROR2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to close contact [%d]. Error [%d]", iRemoveIconList[l], error);
+ }
+ }
+ else
+ {
+ iVoiceTagAddedList.Append( iRemoveIconList[l] );
+ TRAP( error, iPbkHandler->CloseContactL( ETrue ) );
+ if ( error != KErrNone )
+ {
+ RUBY_ERROR2( "CNssContactHandlerImplementation::DoAddNamesCompleted Failed to commit contact [%d]. Error [%d]", iRemoveIconList[l], error);
+ }
+ }
+
+ } // if opened contact successfully
+
+ }
+#endif // VAS_SETS_VOICETAG
+#endif //__SIND_EXTENSIONS
+
+ iRemoveIconList.Close();
+
+ for( TInt k( 0 ); k < iContactQueue.Count(); k++ )
+ {
+ delete iContactQueue[k].iTitle;
+ iContactQueue[k].iTitle = NULL;
+ }
+ iContactQueue.Close();
+
+ if( changedDatabase )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted changed DBx" );
+ WaitUntilNoSyncBackupIsRunning();
+ TRAPD(err, iPbkHandler->CompressL() );
+ if ( err )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesCompleted Contacts DB compression failed. Error [%d]",
+ err);
+ }
+ }
+ else
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted did NOT change DB" );
+ }
+
+ // full resyncs are always tag additions
+ if( initialDeleteTagListCount == iDeleteTagListArray->Count() )
+ {
+ SetState( ECHIdle );
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted Syncing with phonebook generated NO deletions" );
+ // then full resync will not be initiated
+ iConsecutiveErrorFullResync = 0;
+ if ( iEventArray.Count() > 0 )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesCompleted There are still [%d] events in the queue. Call DoHandleEvents for them",
+ iEventArray.Count() );
+ TRAPD( err, DoHandleEventsL() );
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DoAddNamesCompleted. Error [%d] in DoHandleEventsL", err );
+ }
+ }
+ else
+ {
+ // No self-generated events, and no pending events to handle
+ // This is the only place in the code, where everything is completed
+ // and nothing is pending
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted All the updates completed. No DB event is pending" );
+
+ }
+ }
+ else
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoAddNamesCompleted Generated [%d] deletions. Starting to wait for callbacks",
+ iDeleteTagListArray->Count() - initialDeleteTagListCount);
+ }
+
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoAddNamesCompleted completed" );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::NormalChangesHandleError
+// Handle errors which happend when updating normal changes
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::NormalChangesHandleError()
+ {
+ // We can't know, what went wrong.
+ // Stop contact handler, so that the next time the phone is booted
+ // full synchronization happens.
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::NormalChangesHandleError " );
+ TRAPD( err, DoHandleEventFailedL(); );
+ if( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::NormalChangesHandleError DoHandleEventFailedL Left with [%d]", err );
+ }
+ }
+
+//---------------------------------------------------------
+// CNssContactHandlerImplementation::HandleTrainComplete
+// Saves the tag after successful training.
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::HandleTrainComplete( TInt aErrorCode )
+ {
+ if( aErrorCode == KErrNone )
+ {
+ RUBY_DEBUG1( "Training OK for %d:th name.", iTrainCallbackCounter );
+
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleTrainComplete. iState [%d]", iState);
+ RUBY_ASSERT_DEBUG( iState == ECHInitializing ||
+ iState == ECHHandlingNormalChanges,
+ User::Panic( KFile, __LINE__ ) );
+
+ iTrainCallbackCounter++;
+
+ if ( iTrainCallbackCounter == iTagArray.Count() )
+ {
+ if ( iState != ECHInitializing )
+ {
+ // assert at the beginning of the function guarantees,
+ // that iState == ECHHandlingNormalChanges
+ DoAddNamesTrainingFinished();
+ }
+ }
+ }
+
+ else // aErrorCode != KErrNone
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleTrainComplete. Training failed for %d:th name.\n",
+ iTrainCallbackCounter );
+
+ RUBY_ASSERT_DEBUG( iState == ECHInitializing ||
+ iState == ECHHandlingNormalChanges,
+ User::Panic( KFile, __LINE__ ) );
+
+ // Order removal of voice tag icon if present
+ MNssRRD* rrd = iTagArray[iTrainCallbackCounter]->RRD();
+ CArrayFixFlat<TInt>* intArray = rrd->IntArray();
+ RUBY_ASSERT_DEBUG( intArray->Count() >= 2, User::Panic( KFile, __LINE__ ) );
+ if ( intArray->Count() >= 2 )
+ {
+ TInt error = iRemoveIconList.Append( intArray->At(0) );
+ if( error != KErrNone )
+ {
+ RUBY_ERROR2( "CNssContactHandlerImplementation::HandleTrainError Failed to insert [%d] into iRemoveIconList. Error [%d]",
+ intArray->At(0), error);
+ }
+ }
+ else
+ {
+ RUBY_ERROR2( "CNssContactHandlerImplementation::HandleTrainComplete rrd->IntArray()->Count [%d] for iTrainCallbackCounter [%d]",
+ intArray->Count(), iTrainCallbackCounter);
+ }
+
+
+ delete iTagArray[ iTrainCallbackCounter ];
+ iTagArray.Remove( iTrainCallbackCounter );
+
+ if ( iTrainCallbackCounter == iTagArray.Count() )
+ {
+ if ( iState == ECHHandlingNormalChanges )
+ {
+ DoAddNamesTrainingFinished();
+ }
+ else
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::HandleTrainComplete Unexpected state [%d]", iState);
+ }
+ }
+ }
+ }
+
+//---------------------------------------------------------
+// CNssContactHandlerImplementation::HandleTrainError
+// Handles single tag training failure
+//---------------------------------------------------------
+//
+/*void CNssContactHandlerImplementation::HandleTrainError( enum MNssTrainTextEventHandler::TNssTrainResult )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::HandleTrainError Training failed for %d:th name.\n",
+ iTrainCallbackCounter );
+
+ RUBY_ASSERT_DEBUG( iState == ECHInitializing ||
+ iState == ECHHandlingNormalChanges,
+ User::Panic( KFile, __LINE__ ) );
+
+ // Order removal of voice tag icon if present
+ MNssRRD* rrd = iTagArray[iTrainCallbackCounter]->RRD();
+ CArrayFixFlat<TInt>* intArray = rrd->IntArray();
+ RUBY_ASSERT_DEBUG( intArray->Count() >= 2, User::Panic( KFile, __LINE__ ) );
+ if ( intArray->Count() >= 2 )
+ {
+ TInt error = iRemoveIconList.Append( intArray->At(0) );
+ if( error != KErrNone )
+ {
+ RUBY_ERROR2( "CNssContactHandlerImplementation::HandleTrainError Failed to insert [%d] into iRemoveIconList. Error [%d]",
+ intArray->At(0), error);
+ }
+ }
+ else
+ {
+ RUBY_ERROR2( "CNssContactHandlerImplementation::HandleTrainError rrd->IntArray()->Count [%d] for iTrainCallbackCounter [%d]",
+ intArray->Count(), iTrainCallbackCounter);
+ }
+
+
+ delete iTagArray[ iTrainCallbackCounter ];
+ iTagArray.Remove( iTrainCallbackCounter );
+
+ if ( iTrainCallbackCounter == iTagArray.Count() )
+ {
+ if ( iState == ECHHandlingNormalChanges )
+ {
+ DoAddNamesTrainingFinished();
+ }
+ else
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::HandleTrainError Unexpected state [%d]", iState);
+ }
+ }
+ }*/
+
+//---------------------------------------------------------
+// CNssContactHandlerImplementation::FullResyncStart
+// Starts a full synchronization
+//---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::FullResyncStart()
+ {
+ // If we have synchronized earlier, we must delete the old context.
+ if ( iHaveWeEverSynced )
+ {
+ iContextManager->DeleteContext( this, iContext );
+ }
+ else
+ {
+ TRAPD( err, FullResyncCreateContextL() );
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::FullResyncStart. Error [%d] in FullResyncCreateContextL", err );
+ }
+ }
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::FullResyncCreateContextL
+// Full resync: Create a new context
+// (prev: Destroy old context, next:populate the context with tags)
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::FullResyncCreateContextL()
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::FullResyncCreateContextL" );
+ if ( iContext != 0 )
+ {
+ delete iContext;
+ iContext = 0;
+ }
+
+ iContext = iContextManager->CreateContextL();
+ iContext->SetNameL( KNssCHNameDialContext );
+ iContext->SetGlobal( ETrue );
+
+ TBuf8<KClientDataSize> clientDataBuf;
+ iClientData.iLanguage = User::Language();
+ iClientData.iContactSyncId = KErrNotFound;
+
+ TRAPD( error, iClientData.ExternalizeL( clientDataBuf ) );
+ if ( !error )
+ {
+ iContext->SetClientData( clientDataBuf );
+ iContextManager->SaveContext( this, iContext );
+ }
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::FullResyncCreateChanges
+// Create tags and send training requests to VAS.
+// VAS trains them and calls back when it's ready
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::FullResyncCreateChangesL()
+ {
+ RUBY_DEBUG_BLOCK( "CNssContactHandlerImplementation::FullResyncCreateChangesL" );
+
+ WaitUntilNoSyncBackupIsRunning();
+
+ CContactIdArray* ids = iPbkHandler->ContactIdArrayLC();
+
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::FullResyncCreateChanges. iContactQueue.Count() == 0" );
+ RUBY_ASSERT_DEBUG( iContactQueue.Count() == 0, User::Panic( KFile, __LINE__ ) );
+ // Create change events for training
+ for ( TInt k( 0 ); k < ids->Count(); k++ )
+ {
+ TPhonebookEvent event;
+ event.iContactId = (*ids)[k];
+ event.iType = EContactAdded;
+ iEventArray.Append( event );
+ }
+
+ CleanupStack::PopAndDestroy( ids ); // Cleanup stack: empty
+
+ // go to processing events
+ SetState ( ECHIdle );
+ TRAPD( err, DoHandleEventsL() );
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::FullResyncCreateChanges. Error [%d] in DoHandleEventsL", err );
+ }
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::DeleteContextCompleted
+// DeleteContext callback. Deleting a context is part of
+// full synchronization.
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DeleteContextCompleted( TInt aErrorCode )
+ {
+ if( aErrorCode == KErrNone )
+ {
+ // Go to the next phase of full synchronization
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DeleteContextCompleted. iState == ECHFullResync, iState [%d]", iState );
+ RUBY_ASSERT_DEBUG( iState == ECHFullResync, User::Panic( KFile, __LINE__ ) );
+ TRAPD( err, FullResyncCreateContextL() );
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DeleteContextCompleted. Error [%d] in FullResyncCreateContextL", err );
+ }
+ }
+ else
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DeleteContextCompleted failed" );
+ RUBY_ASSERT_DEBUG( iState == ECHFullResync, User::Panic( KFile, __LINE__ ) );
+
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DeleteContextCompleted - can't handle, stopping." );
+ TRAPD( err, DoHandleEventFailedL(); );
+ if( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DeleteContextCompleted Left with [%d]", err);
+ }
+ }
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::CausedByVoiceTagAddition
+// Check if the event was caused by voice tag field adding
+// ------------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::CausedByVoiceTagAddition( const TPhonebookEvent& aEvent )
+ {
+ TBool isVoiceTagAddedEvent (EFalse);
+ if( EContactChanged == aEvent.iType )
+ {
+ for ( TInt i = 0; i < iVoiceTagAddedList.Count(); i++ )
+ {
+ if ( aEvent.iContactId == iVoiceTagAddedList[i] )
+ {
+ isVoiceTagAddedEvent = ETrue;
+ iVoiceTagAddedList.Remove( i-- ); // "i--" because of removal
+ // Continue iterations. We are not sure if voice tag for the given
+ // contact could be changed twice
+ }
+ } // for
+ } // if event type
+ return isVoiceTagAddedEvent;
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::PeriodicCallback
+// Static callback required by Symbian Timer services.
+// ------------------------------------------------------------
+//
+TInt CNssContactHandlerImplementation::PeriodicCallback( TAny* pX )
+ {
+ ((CNssContactHandlerImplementation*)pX)->DoPeriodicCallback();
+ return KErrNone; // useless for CPeriodic
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::DoPeriodicCallback
+// Periodically check if there are some events to process
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoPeriodicCallback()
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoPeriodicCallback" );
+ // Check is needed to eliminate unnesessary TRAP and 64 bit time operations
+ // Note, that we are not checking for PeriodicAction(), but for iPeriodicHandleEventTimer == NULL
+ // PeriodicAction() is a method for detection of start of the action,
+ // while iPeriodicHandleEventTimer == NULL says if periodic action is being executed
+ // right *now*
+
+ if ( ( iState == ECHIdle ) && ( iEventArray.Count() > 0 ) && ( !PeriodicAction() ) )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::DoPeriodicCallback. iState is ECHIdle, there are [%d] events to handle and no periodic action in progress", iEventArray.Count());
+ TRAPD( err, DoHandleEventsL());
+ if ( err != KErrNone )
+ {
+ RUBY_ERROR1( "CNssContactHandlerImplementation::DoPeriodicCallback. Error [%d] in DoHandleEventsL", err );
+ }
+ } // if there are some events
+ else
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::DoPeriodicCallback No events to handle" );
+ }
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::PeriodicAction
+// Detects periodic action.
+// Checks if many events have been received "lately"
+// ---------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::PeriodicAction()
+ {
+ // index of the first recorded call
+ TInt checkedIndex = iEventMomentIndex + 1;
+ checkedIndex = checkedIndex < KEventMomentsSize ? checkedIndex : 0;
+ TTime& checkedTime = iEventMoments[checkedIndex];
+
+ // Check if the first recorded call happened "lately" = within last
+ // KPeriodicActionInterval seconds
+ TTime currentTime;
+ currentTime.UniversalTime();
+ TTimeIntervalMicroSeconds diff = currentTime.MicroSecondsFrom( checkedTime );
+ TBool result = diff < iEndOfPeriodicActionInterval;
+ if ( result == EFalse )
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::PeriodicAction. Periodic action completed." );
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::PeriodicAction. Periodic action completed. iEventMomentIndex [%d], checkedIndex [%d]", iEventMomentIndex, checkedIndex );
+ RUBY_DEBUG1( "currentTime [%d]", I64INT(currentTime.Int64()));
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::PeriodicAction. diff [%d], iEndOfPeriodicActionInterval [%d]", I64INT(diff.Int64()), I64INT(iEndOfPeriodicActionInterval.Int64() ) );
+
+ for( TInt i = 0; i < KEventMomentsSize; i++ )
+ {
+ RUBY_DEBUG2( "checkedIndex[%d] = [%d]", i, I64INT(iEventMoments[i].Int64() ) );
+ }
+
+ // Periodic action completed. Or hasn't even been started
+ // Restore timeout values
+ // iEndOfPeriodicActionInterval = KInitialPeriodicActionInterval;
+ if ( iPeriodicHandleEventTimer != NULL )
+ {
+ // stop the timer, it was started, when periodic action was started
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::PeriodicAction Stopping the periodic action timer" );
+ iPeriodicHandleEventTimer->Cancel();
+ delete iPeriodicHandleEventTimer;
+ iPeriodicHandleEventTimer = NULL;
+ }
+ }
+ else
+ {
+ // Periodic action detected
+ // start the timer if it is not running yet
+ if( iPeriodicHandleEventTimer == NULL )
+ {
+ TRAPD( err, iPeriodicHandleEventTimer = CPeriodic::NewL( EPriorityNormal ) );
+ if( err != KErrNone )
+ {
+ // This is a bad situation, but not the fatal one.
+ // If we cannot use timer to detect end of periodic action,
+ // Let's disable periodic action detection at all
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::PeriodicAction. PeriodicTimer creation failed. Error [%d]. Disabling periodic actions", err);
+ result = EFalse;
+ }
+ else
+ { // Timer was created successfully
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::PeriodicAction Starting the periodic action timer" );
+ iPeriodicHandleEventTimer->Start( KPeriodicTimerInterval, KPeriodicTimerInterval, TCallBack( PeriodicCallback, this ) );
+ }
+ }
+ }
+ return result;
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::UpdatePeriodicActionVars
+// Update variables, used to detect if periodic action is happening
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::UpdatePeriodicActionVars()
+ {
+ if ( ++iEventMomentIndex >= KEventMomentsSize )
+ {
+ iEventMomentIndex = 0;
+ }
+ iEventMoments[iEventMomentIndex].UniversalTime();
+
+ // Update values used to detect end of periodic action
+ TTimeIntervalMicroSeconds32 currInterval = MaximalInterval( iEventMoments, KEventMomentsSize, iEventMomentIndex );
+ // 8 times. At the end of action, this would typically be no longer, than 0.5secs
+ // Note, that this is the interval fo KEventMomentsSize events
+ if ( ( currInterval.Int() << 3 ) >= ( KInitialPeriodicActionInterval ) )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::UpdatePeriodicActionVars. Calculated interval is too big. Reverting periodic interval to the max [%d] mcs", KInitialPeriodicActionInterval);
+ iEndOfPeriodicActionInterval = KInitialPeriodicActionInterval;
+ }
+ else
+ {
+ currInterval = currInterval.Int() << 3;
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::UpdatePeriodicActionVars. Adjusting periodic interval to [%d] mcs", currInterval.Int() );
+ iEndOfPeriodicActionInterval = currInterval.Int();
+ }
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::MaximalInterval
+// Return maximal interval from the array of time moments
+// ------------------------------------------------------------
+//
+TTimeIntervalMicroSeconds32 CNssContactHandlerImplementation::MaximalInterval( const TTime aMoments[], const TInt aMomentsLength, const TInt aLastMomentIndex )
+ {
+ RUBY_ASSERT_DEBUG( aMomentsLength > 1, User::Panic( KFile, __LINE__ ) );
+ TInt currIndex( aLastMomentIndex );
+ TInt prevIndex = currIndex - 1 < 0 ? aMomentsLength - 1 : currIndex - 1;
+ TTimeIntervalMicroSeconds32 result( 0 );
+ TTimeIntervalMicroSeconds currInterval;
+
+ for ( TInt i = 0; i < aMomentsLength - 1; i++ )
+ {
+ currInterval = aMoments[currIndex].MicroSecondsFrom( aMoments[prevIndex] );
+ const TInt64& currIntVal = currInterval.Int64();
+ TBool tooBigValue = currIntVal > KMaxTInt;
+
+ // if high != 0, aMoments were filled in the incorrect way
+ // but we should not bug the user about it
+ if ( tooBigValue )
+ {
+ // this cannot be a negative value.. if moments are recorded in the correct order.
+ // Then non-zero high means, that interval is too big to fit into
+ // TTimeIntervalMicroSeconds32
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::MaximalInterval. Maximal interval is out of bounds" );
+ result = TTimeIntervalMicroSeconds32( KMaxTInt );
+ break;
+ }
+ else
+ {
+ // normal size value
+ // casting is safe - we checked, that it fits into TInt
+ TTimeIntervalMicroSeconds32 currInterval32 ( (TInt)currIntVal );
+
+ if ( currInterval32 > result )
+ {
+ result = currInterval32;
+ }
+ }
+ currIndex = currIndex - 1 < 0 ? aMomentsLength - 1 : currIndex - 1;
+ prevIndex = currIndex - 1 < 0 ? aMomentsLength - 1 : currIndex - 1;
+ } // for
+ return result;
+ }
+
+// ------------------------------------------------------------
+// CNssContactHandlerImplementation::SetState
+// Sets the contact handler state.
+// In debug, panics (via asserts) if state change is not legal
+// ------------------------------------------------------------
+//
+void CNssContactHandlerImplementation::SetState( TContactHandlerState aState )
+ {
+ RUBY_DEBUG2( "CNssContactHandlerImplementation::SetState from [%d] to [%d]", iState, aState);
+ switch( aState )
+ {
+ case ECHFullResync:
+ // no pending events
+ RUBY_ASSERT_DEBUG( ( iDeleteTagListArray == NULL ) || ( iDeleteCallbackCounter == iDeleteTagListArray->Count() ), User::Panic( KFile, __LINE__ ) );
+ RUBY_ASSERT_DEBUG( iSaveCallbackCounter == iTagArray.Count(), User::Panic( KFile, __LINE__ ) );
+
+ // full resyncronization needed again if this full resyncronization fails
+ RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+ break;
+ case ECHHandlingNormalChanges:
+ RUBY_ASSERT_DEBUG( iState == ECHIdle, User::Panic( KFile, __LINE__ ) );
+ RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+ break;
+ case ECHIdle:
+ if ( iDeleteTagListArray != NULL )
+ {
+ RUBY_ASSERT_DEBUG( (iSaveCallbackCounter == iTagArray.Count() ) || ( iDeleteCallbackCounter == iDeleteTagListArray->Count() ), User::Panic( KFile, __LINE__ ) );
+ }
+ else
+ {
+ RUBY_ASSERT_DEBUG( ( iSaveCallbackCounter == iTagArray.Count() ) , User::Panic( KFile, __LINE__ ) );
+ }
+ if( iEventArray.Count() == 0 )
+ {
+ // Idle state and no pending events mean, that contact handler
+ // completed its operations for now
+ TInt errProp = iBusyProperty.Set( EContactsNoTraining );
+ if ( errProp != KErrNone )
+ {
+ RUBY_ERROR1("CNssContactHandlerImplementation::ConstructL() Failed to set property. Error [%d]",
+ errProp );
+ // @todo Not a leaving function -> leave commented out
+ //User::Leave( errProp ) ;
+ }
+ }
+
+ // all changes done, no need for resyncronization
+ RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( EFalse ) );
+
+ break;
+ default:
+ RUBY_ERROR0( "CNssContactHandlerImplementation::SetState Unexpected or unknown state" );
+ RUBY_ASSERT_DEBUG( EFalse, User::Panic( KFile, __LINE__ ) );
+
+ // failure, full resyncronization needed
+ RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+ break;
+ }
+ // actually switch state
+ iState = aState;
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DataSyncStateChanged
+//
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DataSyncStateChanged( TBool aRunning )
+ {
+ if( !aRunning && ( iSyncBackupWaiter->IsStarted() ) )
+ {
+ RUBY_DEBUG0( "DataSyncStateChanged Sync became inactive and CH was waiting for it" );
+ iSyncBackupWaiter->AsyncStop();
+ }
+ }
+
+// ---------------------------------------------------------
+// Is called when backup/restore status is changed and when backup/restore
+// state observation was just started
+// @param aRunning ETrue if some backup/restore action is in progress
+// EFalse otherwise
+// @param aRestoreType ETrue if the event is restore related
+// EFalse if the event is backup related
+// @see CNssChBackupObserver
+// ---------------------------------------------------------
+void CNssContactHandlerImplementation::BackupRestoreStateChanged( TBool aRestoreType,
+ TBool aRunning )
+ {
+ RUBY_DEBUG2( "BackupRestoreStateChanged aRestoreType [%d], aRunning [%d]",
+ aRestoreType, aRunning );
+
+ if( aRestoreType && aRunning )
+ {
+ RUBY_DEBUG0( "Setting the Full Resync CR flag" );
+ RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+ }
+
+ if( !aRunning && ( iSyncBackupWaiter->IsStarted() ) )
+ {
+ RUBY_DEBUG0( "BackupRestoreStateChanged b/r became inactive and CH was waiting for it" );
+ iSyncBackupWaiter->AsyncStop();
+ }
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::HandleNotifyInt
+//
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::HandleNotifyInt( TUint32 aId, TInt aNewValue )
+ {
+ RUBY_DEBUG0( "" );
+
+ if ( aId == KSRSFFullResyncNeeded )
+ {
+ if ( aNewValue == KImmediateResync )
+ {
+ RUBY_DEBUG0( "Immediate full resync requested" );
+
+ // Write the previous value back to the cenrep
+ TRAP_IGNORE( SetFullResyncCrFlagL( iResyncAtBoot ) );
+
+ // Create an event that will launch full resync
+ TPhonebookEvent event;
+ event.iType = ENullEvent;
+ event.iContactId = 0;
+
+ iEventArray.Append( event );
+
+ // Start resync if nothing is ongoing
+ if ( iState == ECHIdle )
+ {
+ TRAP_IGNORE( DoHandleEventsL() );
+ }
+ }
+ }
+ }
+
+// ---------------------------------------------------------
+// Sets the central repository full resync flag
+// ---------------------------------------------------------
+void CNssContactHandlerImplementation::SetFullResyncCrFlagL( TBool aResyncNeeded )
+ {
+ RUBY_DEBUG_BLOCKL("");
+ User::LeaveIfError( iRepository->Set( KSRSFFullResyncNeeded, aResyncNeeded ) );
+ iResyncAtBoot = aResyncNeeded;
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::WaitUntilNoSyncBackupIsRunning
+//
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::WaitUntilNoSyncBackupIsRunning()
+ {
+ RUBY_DEBUG0( "WaitUntilNoSyncBackupIsRunning start" );
+ RUBY_ASSERT_DEBUG( !iSyncBackupWaiter->IsStarted(), User::Panic( KFile, __LINE__ ) );
+
+ // Disconnection from phonebook should happen only once and only if b/r is running
+ TBool disconnectedFromPb = EFalse;
+
+ // Check the values until both data sync and backup/restore are not active
+ while( iDataSyncWatcher->InProgress() || iBackupObserver->InProgress() )
+ {
+ RUBY_DEBUG2( "WaitUntilNoSyncBackupIsRunning Have to wait. data sync [%d], backup/restore [%d]",
+ iDataSyncWatcher->InProgress(), iBackupObserver->InProgress() );
+ if( !iSyncBackupWaiter->IsStarted() )
+ {
+ if( (!disconnectedFromPb) && iBackupObserver->InProgress() )
+ {
+ RUBY_DEBUG0( "WaitUntilNoSyncBackupIsRunning Disconnecting from phonebook" );
+ DisconnectFromPhonebook();
+ disconnectedFromPb = ETrue;
+ }
+ iSyncBackupWaiter->Start();
+ }
+ else
+ {
+ RUBY_ERROR0( "WaitUntilNoSyncBackupIsRunning Attempt to start already active iSyncBackupWaiter. Huge error!" );
+ }
+ }
+
+ if( disconnectedFromPb )
+ {
+ RUBY_DEBUG0( "WaitUntilNoSyncBackupIsRunning Reconnecting to phonebook" );
+ TRAP_IGNORE( ConnectToPhonebookL() );
+ }
+
+ RUBY_DEBUG0( "WaitUntilNoSyncBackupIsRunning end" );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::GetContactL
+//
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::GetContactL( TContactItemId aContactId, TBool aReadContact )
+ {
+ RUBY_DEBUG1( "CNssContactHandlerImplementation::GetContactL. contact id [%d]", aContactId );
+ TInt err = KErrNone;
+ // Number of attempts tries
+ TInt attemptsTried( 0 );
+ do
+ {
+ if( err == KErrInUse ) // i.e. not the first cycle iteration
+ {
+ RUBY_DEBUG2( "GetContactL Contact [%d] is locked. Waiting one more second. Retrial [%d]",
+ aContactId, attemptsTried);
+ // start periodic timer
+ if( attemptsTried == 1) // i.e exactly the second cycle iteration
+ {
+ RUBY_DEBUG0( "GetContactL Creating periodic timer" );
+ iContactWaitTimer = CPeriodic::NewL( EPriorityNormal );
+ iContactWaitTimer->Start( KContactWaitInterval, KContactWaitInterval, TCallBack( ContactWaitCallback, this ) );
+ }
+ RUBY_DEBUG0( "GetContactL Starting the inner cycle waiting" );
+ iContactWaiter->Start( );
+ RUBY_DEBUG0( "GetContactL Waiting completed, trying to open contact again" );
+ }
+
+ TRAP( err, iPbkHandler->FindContactL( aContactId, aReadContact ) );
+
+ attemptsTried++;
+ // -1 means forever
+ if( ( KMaxContactLockRetrials != -1 ) && ( attemptsTried >= KMaxContactLockRetrials ) )
+ {
+ RUBY_DEBUG1( "GetContactL Tried for [%d] times, still no success", attemptsTried );
+ break;
+ }
+ }
+ while( err == KErrInUse );
+ if( iContactWaitTimer != NULL )
+ {
+ iContactWaitTimer->Cancel();
+ delete iContactWaitTimer;
+ iContactWaitTimer = NULL;
+ }
+ // Propagate all the leaves, BUT KErrInUse
+ if( ( err != KErrInUse ) && ( err != KErrNone ) )
+ {
+ User::LeaveIfError( err );
+ }
+ if( err == KErrInUse )
+ {
+ RUBY_DEBUG1( "GetContactL Failed to lock contact [%d]. Disabling CH, will resync on reboot", aContactId );
+ DisableEventHandling();
+ User::Leave( KErrInUse );
+ }
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::GetContactL completed" );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::ReadContactL
+//
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::ReadContactL( TContactItemId aContactId )
+ {
+ GetContactL( aContactId, ETrue );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::OpenContactL
+//
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::OpenContactL( TContactItemId aContactId )
+ {
+ GetContactL( aContactId, EFalse );
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::ContactWaitCallback
+//
+// ---------------------------------------------------------
+//
+TInt CNssContactHandlerImplementation::ContactWaitCallback( TAny* pX )
+ {
+ ((CNssContactHandlerImplementation*)pX)->DoContactWaitCallback();
+ return KErrNone; // useless for CPeriodic
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::DoContactWaitCallback
+//
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::DoContactWaitCallback()
+ {
+ iContactWaiter->AsyncStop();
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::NoDiskSpace
+// Checks if there is not enough disk space to finish the
+// operation. Returns ETrue if there is no space.
+// ---------------------------------------------------------
+//
+TBool CNssContactHandlerImplementation::NoDiskSpace()
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::NoDiskSpace" );
+ TBool diskSpace( EFalse );
+ TRAP_IGNORE( diskSpace = SysUtil::FFSSpaceBelowCriticalLevelL( &iFSession, KIncreaseOfDatabaseSizes ) );
+
+ if ( diskSpace )
+ {
+ RUBY_TRAP_IGNORE( SetFullResyncCrFlagL( ETrue ) );
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::NoDiskSpace NOT enough disk space available" );
+ return ETrue;
+ }
+ else
+ {
+ RUBY_DEBUG0( "CNssContactHandlerImplementation::NoDiskSpace There is enough disk space" );
+ return EFalse;
+ }
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::TrimName
+// Substitutes separator characters in descriptor with
+// white spaces. Removes extra withspaces.
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::TrimName( HBufC* aBuf )
+ {
+ TPtr ptr = aBuf->Des();
+
+ for ( TInt findIndex = 0; findIndex < aBuf->Des().Length(); findIndex++ )
+ {
+ if ( ptr[findIndex] == iSeparator )
+ {
+ ptr[findIndex] = ' ';
+ }
+ }
+
+ ptr.TrimAll();
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::AppendName
+// Appends name to given buffer
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::AppendName( HBufC* aBuf, HBufC* aName )
+ {
+ if ( aName->Length() != 0 )
+ {
+ aBuf->Des().Append( iSeparator );
+ aBuf->Des().Append( KNameTrainingIndex );
+ aBuf->Des().Append( aName->Des() );
+ }
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::AppendExtensionMarker
+// Appends marker for the extensions
+// Parameters should be in format "%U text message"
+// In which case output is "%U_2text message"
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::AppendExtensionMarker( TDes& aDes )
+ {
+ if ( aDes.Length() > 0 )
+ {
+ TInt insertPosition( 0 );
+ if ( aDes.Find( KExtensionFormatString ) == 0 )
+ {
+ // Find the first space and insert marker to that position
+ TInt firstSpace( 0 );
+ firstSpace = aDes.Find( _L(" ") );
+ if ( firstSpace == 2 ) // Next character after KExtensionFormatString
+ {
+ aDes.Delete( firstSpace, 1 );
+ }
+ insertPosition = 2;
+ }
+ // Insert marker
+ aDes.Insert( insertPosition, KExtensionTrainingIndex );
+ aDes.Insert( insertPosition, KNameSeparator );
+ }
+ }
+
+/**
+* Establishes the connection to the contact engine and
+* starts listening to it
+*/
+void CNssContactHandlerImplementation::ConnectToPhonebookL()
+ {
+ RUBY_ASSERT_DEBUG( (!iPbkHandler), User::Leave( KErrNotReady ) );
+ iPbkHandler = CVasVPbkHandler::NewL();
+ iPbkHandler->InitializeL();
+ iPbkHandler->CreateContactObserverL( this );
+ }
+
+/**
+* Clears the connection to the contact engine
+*/
+void CNssContactHandlerImplementation::DisconnectFromPhonebook()
+ {
+ delete iPbkHandler;
+ iPbkHandler = NULL;
+ }
+
+#ifdef __SIND_EXTENSIONS
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::ReadExtensionsL
+// Reads the extension resource file
+// ---------------------------------------------------------
+//
+void CNssContactHandlerImplementation::ReadExtensionsL()
+ {
+ // using StringLoader should be used, but there is no
+ // access to CCoeEnv
+
+
+ // letters for drives in search order
+ const TBuf<1> KResourceDrivers = _L("z");
+ _LIT( KResourceFileName, "nssvascontacthandler.rsc" );
+
+ // number of extension commands in the resource file
+ const TInt KExtensionCommandCount = 8;
+
+ // load resources
+ RResourceFile resourceFile;
+ RFs fs;
+ CleanupClosePushL( fs );
+ TBool found( EFalse );
+ User::LeaveIfError( fs.Connect() );
+
+
+ TFileName name;
+ TInt i( 0 );
+ // use the first drive
+ name.Append( KResourceDrivers[i] );
+ name.Append(':');
+ name.Append( KDC_RESOURCE_FILES_DIR );
+ name.Append( KResourceFileName );
+
+ while ( !found && i < KResourceDrivers.Length() )
+ {
+ name[0] = KResourceDrivers[i++];
+
+ BaflUtils::NearestLanguageFile( fs, name );
+
+ if ( BaflUtils::FileExists(fs, name) )
+ {
+ // open resource
+ resourceFile.OpenL( fs, name );
+ CleanupClosePushL( resourceFile );
+ resourceFile.ConfirmSignatureL();
+ found = ETrue;
+ }
+ }
+
+ if ( !found )
+ {
+ User::Leave( KErrNotFound );
+ }
+
+ for ( TInt extensionIndex( 0 ); extensionIndex < KExtensionCommandCount; extensionIndex++ )
+ {
+ TExtension extension;
+ TResourceReader reader;
+ TBool extentionSupported( ETrue );
+
+ switch ( extensionIndex )
+ {
+
+ case 0:
+ reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_MOBILE ) );
+ extension.iId = EPbkFieldIdPhoneNumberMobile;
+ extension.iAction = EDial;
+ extension.iCommand = EMobileCommand;
+ break;
+
+ case 1:
+ reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_MESSAGE ) );
+ extension.iId = EPbkFieldIdPhoneNumberMobile;
+ extension.iAction = ENewMessage;
+ extension.iCommand = EMessageCommand;
+ break;
+
+ case 2:
+ reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_GENERAL ) );
+ extension.iId = EPbkFieldIdPhoneNumberGeneral;
+ extension.iAction = EDial;
+ extension.iCommand = EGeneralCommand;
+ break;
+
+ case 3:
+ reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_WORK ) );
+ extension.iId = EPbkFieldIdPhoneNumberWork;
+ extension.iAction = EDial;
+ extension.iCommand = EWorkCommand;
+ break;
+
+ case 4:
+ reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_HOME ) );
+ extension.iId = EPbkFieldIdPhoneNumberHome;
+ extension.iAction = EDial;
+ extension.iCommand = EHomeCommand;
+ break;
+
+ case 5:
+ if ( iVideoCallFeatureSupported )
+ {
+ reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_VIDEO ) );
+ extension.iId = EPbkFieldIdPhoneNumberVideo;
+ extension.iAction = EDial;
+ extension.iCommand = EVideoCommand;
+ }
+ else
+ {
+ extentionSupported = EFalse;
+ }
+ break;
+
+ case 6:
+ reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_EMAIL ) );
+ extension.iId =EPbkFieldIdEmailAddress ;
+ extension.iAction = ENewEmail;
+ extension.iCommand = EEmailCommand;
+ break;
+
+ case 7:
+ if ( iVoIPFeatureSupported )
+ {
+ reader.SetBuffer( resourceFile.AllocReadLC( R_SIND_EXTENSION_VOIP ) );
+ extension.iId = EPbkFieldIdVOIP;
+ extension.iAction = EDial;
+ extension.iCommand = EVoipCommand;
+ }
+ else
+ {
+ extentionSupported = EFalse;
+ }
+ break;
+
+ default:
+ User::Leave( KErrGeneral ); // !!! @todo: PANIC
+ }
+
+ if ( extentionSupported )
+ {
+ extension.iText = reader.ReadHBufCL();
+ CleanupStack::PushL( extension.iText );
+ User::LeaveIfError( iExtensionList.Append( extension ) );
+ CleanupStack::Pop( extension.iText );
+ CleanupStack::PopAndDestroy(); // AllocReadLC
+ }
+ }
+
+ CleanupStack::PopAndDestroy( &resourceFile );
+ CleanupStack::PopAndDestroy( &fs );
+
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::FindExtensionField
+// Finds the extension field based on priority lists
+// ---------------------------------------------------------
+//
+TInt CNssContactHandlerImplementation::FindExtensionField( TVasExtensionAction aAction,
+ TPbkFieldId aDefaultFieldId )
+ {
+ TInt found = KErrNotFound;
+
+ switch ( aDefaultFieldId )
+ {
+ case EPbkFieldIdPhoneNumberVideo:
+ {
+ // Use defaults for video extension
+ found = ExtensionFieldFindLoop( KVideoExtensionFields, KVideoExtensionFieldsCount );
+ break;
+ }
+
+ case EPbkFieldIdPhoneNumberMobile:
+ {
+ if ( aAction == ENewMessage )
+ {
+ // Use defaults for message extension
+ found = ExtensionFieldFindLoop( KMessageExtensionFields, KMessageExtensionFieldsCount );
+ }
+ else
+ {
+ TRAP( found, iPbkHandler->FindFieldL( aDefaultFieldId ) );
+ }
+
+ break;
+ }
+
+ case EPbkFieldIdVOIP:
+ {
+ // Use defaults for VOIP extension
+ found = ExtensionFieldFindLoop( KVOIPExtensionFields, KVOIPExtensionFieldsCount );
+
+ break;
+ }
+
+ default:
+ {
+ TRAP( found, iPbkHandler->FindFieldL( aDefaultFieldId ) );
+ break;
+ }
+ }
+ return found;
+ }
+
+// ---------------------------------------------------------
+// CNssContactHandlerImplementation::ExtensionFieldFindLoop
+// Finds the extension from the prioroty list arrays
+// ---------------------------------------------------------
+//
+TInt CNssContactHandlerImplementation::ExtensionFieldFindLoop( const TPbkFieldId* const aArray,
+ TInt aCount )
+ {
+ TInt found = KErrNotFound;
+
+ for ( TInt iCounter = 0; iCounter < aCount; iCounter++ )
+ {
+ TRAP( found, iPbkHandler->FindFieldL( aArray[iCounter] ) );
+ if ( found == KErrNone )
+ {
+ break;
+ }
+ }
+ return found;
+ }
+
+#endif // __SIND_EXTENSIONS
+
+// End of File