diff -r 5b6f26637ad3 -r f4a778e096c2 phonebookui/Phonebook2/Presentation/src/CPbk2DuplicateContactFinder.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/phonebookui/Phonebook2/Presentation/src/CPbk2DuplicateContactFinder.cpp Wed Sep 01 12:29:52 2010 +0100 @@ -0,0 +1,502 @@ +/* +* Copyright (c) 2006-2007 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: A class for finding contact duplicates from stores +* +*/ + + +#include + +// From Phonebook2 +#include +#include + +// From Virtual Phonebook +#include +#include +#include +#include +#include +#include +#include +#include + +#include +namespace + { + #ifdef _DEBUG + enum TPanicCode + { + EPanic_RetrieveContactL_OOB = 1 + }; + + void Panic( TPanicCode aPanic ) + { + _LIT( KPanicCat, "CPbk2DuplicateContactFinder" ); + User::Panic( KPanicCat(), aPanic ); + } + #endif // _DEBUG + } + +enum TPbk2DuplicateContactFinderState + { + ENoDuplicatesFound, + EStartFind, + ERetrieveContact, + EComplete + }; + +const TInt KNameFormattingFlags = + MPbk2ContactNameFormatter::EPreserveLeadingSpaces; + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::CPbk2DuplicateContactFinder +// --------------------------------------------------------------------------- +// +CPbk2DuplicateContactFinder::CPbk2DuplicateContactFinder( + CVPbkContactManager& aContactManager, + MPbk2ContactNameFormatter& aNameFormatter, + const MVPbkFieldTypeList& aFieldTypesForFind, + RPointerArray& aDuplicateContacts ): + CActive( EPriorityStandard ), + iContactManager( aContactManager ), + iNameFormatter( aNameFormatter ), + iFieldTypesForFind( aFieldTypesForFind ), + iFindText( KNullDesC ), + iDuplicates( aDuplicateContacts ) + { + } + + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::ConstructL +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::ConstructL() + { + CActiveScheduler::Add( this ); + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::NewL +// --------------------------------------------------------------------------- +// +EXPORT_C CPbk2DuplicateContactFinder* CPbk2DuplicateContactFinder::NewL( + CVPbkContactManager& aContactManager, + MPbk2ContactNameFormatter& aNameFormatter, + const MVPbkFieldTypeList& aFieldTypesForFind, + RPointerArray& aDuplicateContacts ) + { + CPbk2DuplicateContactFinder* self = + new( ELeave ) CPbk2DuplicateContactFinder( aContactManager, + aNameFormatter, aFieldTypesForFind, aDuplicateContacts ); + CleanupStack::PushL( self ); + self->ConstructL(); + CleanupStack::Pop( self ); + return self; + } + +// --------------------------------------------------------------------------- +// destructor +// --------------------------------------------------------------------------- +// +CPbk2DuplicateContactFinder::~CPbk2DuplicateContactFinder() + { + Cancel(); + delete iContactOperation; + delete iFindResults; + delete iContactTitle; + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::StartL +// --------------------------------------------------------------------------- +// +EXPORT_C void CPbk2DuplicateContactFinder::StartL( + const MVPbkBaseContact& aContact, + MVPbkContactStore& aStore, + MPbk2DuplicateContactObserver& aObserver, + TInt aMaxDuplicatesToFind ) + { + iStore = &aStore; + StartL( aContact, aObserver, aMaxDuplicatesToFind ); + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::StartL +// --------------------------------------------------------------------------- +// +EXPORT_C void CPbk2DuplicateContactFinder::StartL( + const MVPbkBaseContact& aContact, + MPbk2DuplicateContactObserver& aObserver, + TInt aMaxDuplicatesToFind ) + { + iObserver = &aObserver; + iContactToCompare = &aContact; + iContactIndex = 0; + iMaxDuplicatesToFind = aMaxDuplicatesToFind; + + iState = ENoDuplicatesFound; + iFindText.Set( FindText() ); + if ( iFindText.Length() > 0 ) + { + iState = EStartFind; + } + IssueRequest(); + } + +// --------------------------------------------------------------------------- +// From class CActive. +// CPbk2DuplicateContactFinder::RunL +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::RunL() + { + switch ( iState ) + { + case EStartFind: + { + StartFindL(); + break; + } + case ERetrieveContact: + { + RetrieveContactL(); + break; + } + case EComplete: + { + CompleteL(); + break; + } + case ENoDuplicatesFound: // FALLTHROUGH + default: + { + iObserver->DuplicateFindComplete(); + break; + } + } + } + +// --------------------------------------------------------------------------- +// From class CActive. +// CPbk2DuplicateContactFinder::DoCancel +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::DoCancel() + { + DestroyOperation(); + } + +// --------------------------------------------------------------------------- +// From class CActive. +// CPbk2DuplicateContactFinder::RunError +// --------------------------------------------------------------------------- +// +TInt CPbk2DuplicateContactFinder::RunError( TInt aError ) + { + iObserver->DuplicateFindFailed( aError ); + return KErrNone; + } + +// --------------------------------------------------------------------------- +// From class MVPbkContactFindObserver. +// CPbk2DuplicateContactFinder::FindCompleteL +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::FindCompleteL( + MVPbkContactLinkArray* aResults ) + { + iFindResults = aResults; + iState = ENoDuplicatesFound; + if ( !LastContactRetrieved() ) + { + iState = ERetrieveContact; + } + + IssueRequest(); + } + +// --------------------------------------------------------------------------- +// From class MVPbkContactFindObserver. +// CPbk2DuplicateContactFinder::FindFailed +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::FindFailed( TInt aError ) + { + iObserver->DuplicateFindFailed( aError ); + } + +// --------------------------------------------------------------------------- +// From class MVPbkSingleContactOperationObserver. +// CPbk2DuplicateContactFinder::VPbkSingleContactOperationComplete +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::VPbkSingleContactOperationComplete( + MVPbkContactOperationBase& /*aOperation*/, + MVPbkStoreContact* aContact ) + { + TRAPD( res, CheckDuplicateL( aContact ) ); + + ++iContactIndex; + if ( res != KErrNone ) + { + iObserver->DuplicateFindFailed( res ); + } + else if ( LastContactRetrieved() ) + { + iState = EComplete; + IssueRequest(); + } + else + { + // Continue retrieving + IssueRequest(); + } + } + +// --------------------------------------------------------------------------- +// From class MVPbkSingleContactOperationObserver. +// CPbk2DuplicateContactFinder::VPbkSingleContactOperationFailed +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::VPbkSingleContactOperationFailed( + MVPbkContactOperationBase& /*aOperation*/, TInt aError ) + { + iObserver->DuplicateFindFailed( aError ); + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::IssueRequest +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::IssueRequest() + { + TRequestStatus* st = &iStatus; + User::RequestComplete( st, KErrNone ); + SetActive(); + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::FindText +// --------------------------------------------------------------------------- +// +TPtrC CPbk2DuplicateContactFinder::FindText() + { + const MVPbkBaseContactFieldCollection& fields = + iContactToCompare->Fields(); + // Find a text from the contact in the order of iFieldTypesForFind. + // the first data that is found is used as a find text + const TInt findTypeCount = iFieldTypesForFind.FieldTypeCount(); + for ( TInt i = 0; i < findTypeCount; ++i ) + { + const MVPbkFieldType& findType = iFieldTypesForFind.FieldTypeAt( i ); + const TInt fieldCount = fields.FieldCount(); + for ( TInt j = 0; j < fieldCount; ++j ) + { + const MVPbkFieldType* type = + fields.FieldAt( j ).BestMatchingFieldType(); + if ( type && findType.IsSame( *type ) ) + { + const MVPbkContactFieldData& data = + fields.FieldAt( j ).FieldData(); + if ( data.DataType() == EVPbkFieldStorageTypeText ) + { + TPtrC text( MVPbkContactFieldTextData::Cast( + data ).Text() ); + if ( text.Length() > 0 ) + { + return text; + } + } + } + } + } + // No find text -> no duplicates for contact. + return TPtrC( KNullDesC ); + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::FindOperationL +// --------------------------------------------------------------------------- +// +MVPbkContactOperationBase* CPbk2DuplicateContactFinder::FindOperationL( + const TDesC& aFindText ) + { + if ( iStore ) + { + MVPbkContactOperation* op = iStore->CreateFindOperationL( aFindText, + iFieldTypesForFind, *this ); + CleanupDeletePushL( op ); + op->StartL(); + CleanupStack::Pop(); // op + return op; + } + else + { + return iContactManager.FindL( aFindText, + iFieldTypesForFind, *this ); + } + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::StartFindL +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::StartFindL() + { + delete iFindResults; + iFindResults = NULL; + + delete iContactTitle; + iContactTitle = NULL; + iContactTitle = iNameFormatter.GetContactTitleL( + iContactToCompare->Fields(), KNameFormattingFlags ); + + DestroyOperation(); + iContactOperation = FindOperationL( iFindText ); + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::RetrieveContactL +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::RetrieveContactL() + { + DestroyOperation(); + __ASSERT_DEBUG( iFindResults->Count() > iContactIndex, + Panic( EPanic_RetrieveContactL_OOB ) ); + iContactOperation = iContactManager.RetrieveContactL( + iFindResults->At( iContactIndex ), *this ); + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::CompleteL +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::CompleteL() + { + iObserver->DuplicateFindComplete(); + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::LastContactRetrieved +// --------------------------------------------------------------------------- +// +TBool CPbk2DuplicateContactFinder::LastContactRetrieved() + { + if ( iDuplicates.Count() >= iMaxDuplicatesToFind || + iContactIndex >= iFindResults->Count() ) + { + return ETrue; + } + return EFalse; + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::DestroyOperation +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::DestroyOperation() + { + delete iContactOperation; + iContactOperation = NULL; + } + +// --------------------------------------------------------------------------- +// CPbk2DuplicateContactFinder::CheckDuplicateL +// --------------------------------------------------------------------------- +// +void CPbk2DuplicateContactFinder::CheckDuplicateL( + MVPbkStoreContact* aContact ) + { + aContact->PushL(); + + TBool duplicate = EFalse; + HBufC* candidateTitle = iNameFormatter.GetContactTitleOrNullL( + aContact->Fields(), KNameFormattingFlags ); + // If the aContact is not a group and it has a title, compare it with iContactTitle. + // This is because some group has the same name with the contact and they can't be + // considered as duplicate. + if ( !aContact->Group() && candidateTitle ) + { + CleanupStack::PushL( candidateTitle ); + if ( iContactTitle->CompareF( *candidateTitle ) == 0 ) + { + // Compare the first and last name. + if ( IsFieldMatched( aContact, R_VPBK_FIELD_TYPE_FIRSTNAME ) && + IsFieldMatched( aContact, R_VPBK_FIELD_TYPE_LASTNAME ) ) + { + duplicate = ETrue; + } + } + CleanupStack::PopAndDestroy( candidateTitle ); + } + + if ( duplicate ) + { + iDuplicates.AppendL( aContact ); + CleanupStack::Pop(); // aContact + } + else + { + CleanupStack::PopAndDestroy(); // aContact + } + } + +TBool CPbk2DuplicateContactFinder::IsFieldMatched( const MVPbkBaseContact* aContact, TInt aFieldId ) + { + TBool result = EFalse; + + const MVPbkBaseContactField* compareContactField = FindFieldById( iContactToCompare, aFieldId ); + const MVPbkBaseContactField* contactField = FindFieldById( aContact, aFieldId ); + if ( NULL == compareContactField && NULL == contactField ) + { + result = ETrue; + } + else if ( compareContactField && contactField ) + { + const TDesC& compareContactFieldText = + MVPbkContactFieldTextData::Cast(compareContactField->FieldData()).Text(); + const TDesC& contactFieldText = + MVPbkContactFieldTextData::Cast(contactField->FieldData()).Text(); + if ( compareContactFieldText.CompareF( contactFieldText ) == 0 ) + { + result = ETrue; + } + } + + return result; + } + +const MVPbkBaseContactField* CPbk2DuplicateContactFinder::FindFieldById( const MVPbkBaseContact* aContact, + TInt aFieldId ) + { + const TInt fieldCount = aContact->Fields().FieldCount(); + const MVPbkBaseContactFieldCollection& fieldSet = aContact->Fields(); + for ( TInt i = 0; i < fieldCount; ++i ) + { + const MVPbkBaseContactField& field = fieldSet.FieldAt(i); + if ( field.BestMatchingFieldType() ) + { + TInt fieldTypeId = field.BestMatchingFieldType()->FieldTypeResId(); + if ( fieldTypeId == aFieldId ) + { + return &field; + } + } + } + return NULL; + } +