--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/omads/omadsextensions/adapters/contactsgroup/src/contactsgrpdatastore.cpp Tue Aug 31 15:05:37 2010 +0300
@@ -0,0 +1,1268 @@
+/*
+* Copyright (c) 2009-2010 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: Part of SyncML Data Synchronization Plug In Adapter
+*
+*/
+
+
+#include <sysutil.h>
+#include <cntfldst.h>
+
+#include "contactsgrpdatastore.h"
+#include "contactsgrpconverter.h"
+#include "changefinder.h"
+#include "contactsgrpdataproviderdefs.h"
+#include "logger.h"
+
+const TInt KDefaultBufferSize = 1024;
+const TInt KDataBufferNotReady = -1;
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::CContactsGrpDataStore
+// C++ default constructor can NOT contain any code, that might leave.
+// -----------------------------------------------------------------------------
+CContactsGrpDataStore::CContactsGrpDataStore() :
+ iKey( TKeyArrayFix( _FOFF( TNSmlSnapshotItem, ItemId() ), ECmpTInt ) )
+ {
+ TRACE_FUNC;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::ConstructL
+// Symbian 2nd phase constructor, can leave.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::ConstructL()
+ {
+ TRACE_FUNC_ENTRY;
+
+ User::LeaveIfError( iFs.Connect() );
+
+ iNewItems = new ( ELeave ) CNSmlDataItemUidSet;
+ iDeletedItems = new ( ELeave ) CNSmlDataItemUidSet;
+ iUpdatedItems = new ( ELeave ) CNSmlDataItemUidSet;
+ iEmptyList = new ( ELeave ) CNSmlDataItemUidSet;
+
+ iConverter = CContactsGrpConverter::NewL();
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::~CContactsGrpDataStore
+// Destructor
+// -----------------------------------------------------------------------------
+CContactsGrpDataStore::~CContactsGrpDataStore()
+ {
+ TRACE_FUNC_ENTRY;
+
+ delete iDataBuffer;
+ delete iNewItems;
+ delete iDeletedItems;
+ delete iUpdatedItems;
+ delete iEmptyList;
+
+ if ( iChangeFinder )
+ {
+ TRAPD( error, iChangeFinder->CloseL() );
+ if ( error != KErrNone )
+ {
+ LOGGER_WRITE_1( "iChangeFinder->CloseL() leaved with %d", error );
+ }
+ }
+ delete iChangeFinder;
+
+ delete iConverter;
+
+ delete iContactsDb;
+
+ iFs.Close();
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::NewL
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+CContactsGrpDataStore* CContactsGrpDataStore::NewL()
+ {
+ TRACE_FUNC_ENTRY;
+ CContactsGrpDataStore* self = CContactsGrpDataStore::NewLC();
+ CleanupStack::Pop( self );
+ TRACE_FUNC_EXIT;
+ return self;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::NewLC
+// Two-phased constructor.
+// -----------------------------------------------------------------------------
+CContactsGrpDataStore* CContactsGrpDataStore::NewLC()
+ {
+ TRACE_FUNC_ENTRY;
+ CContactsGrpDataStore* self = new ( ELeave ) CContactsGrpDataStore();
+ CleanupStack::PushL( self );
+ self->ConstructL();
+ TRACE_FUNC_EXIT;
+ return self;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoOpenL
+// Opens database.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoOpenL( const TDesC& /*aStoreName*/,
+ MSmlSyncRelationship& aContext, TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ if ( iContactsDb )
+ {
+ // already opened
+ User::RequestComplete( iCallerStatus, KErrInUse );
+ LOGGER_WRITE( "CContactsGrpDataStore::DoOpenL failed with KErrInUse." );
+ return;
+ }
+
+ // Create ChangeFinder
+ if ( iChangeFinder )
+ {
+ delete iChangeFinder;
+ iChangeFinder = NULL;
+ }
+ iChangeFinder = CChangeFinder::NewL( aContext, iKey, iHasHistory );
+
+ iContactsDb = CContactDatabase::OpenL();
+
+ RegisterSnapshotL();
+
+ User::RequestComplete( iCallerStatus, KErrNone );
+ TRACE_FUNC_EXIT;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCancelRequest
+// Not supported, does nothing.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCancelRequest()
+ {
+ TRACE_FUNC;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoStoreName
+// Returns the name of the DataStore
+// -----------------------------------------------------------------------------
+const TDesC& CContactsGrpDataStore::DoStoreName() const
+ {
+ TRACE_FUNC;
+
+ if ( iContactsDb )
+ {
+ return KContactsGrpStoreName;
+ }
+ else
+ {
+ return KNullDesC;
+ }
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoBeginTransactionL
+// Transactions are not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoBeginTransactionL()
+ {
+ TRACE_FUNC;
+ User::Leave( KErrNotSupported );
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitTransactionL
+// Transactions are not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitTransactionL( TRequestStatus& aStatus )
+ {
+ TRACE_FUNC;
+ iCallerStatus = &aStatus;
+ User::RequestComplete( iCallerStatus, KErrNotSupported );
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoRevertTransaction
+// Transactions are not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoRevertTransaction( TRequestStatus& aStatus )
+ {
+ TRACE_FUNC;
+ iCallerStatus = &aStatus;
+ User::RequestComplete( iCallerStatus, KErrNotSupported );
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoBeginBatchL
+// Batching is not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoBeginBatchL()
+ {
+ TRACE_FUNC;
+ User::Leave( KErrNotSupported );
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitBatchL
+// Batching is not supported
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitBatchL( RArray<TInt>& /*aResultArray*/, TRequestStatus& aStatus )
+ {
+ TRACE_FUNC;
+ iCallerStatus = &aStatus;
+ User::RequestComplete( iCallerStatus, KErrNotSupported );
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCancelBatch
+// Batching is not supported
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCancelBatch()
+ {
+ TRACE_FUNC;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoSetRemoteStoreFormatL
+// Not supported
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoSetRemoteStoreFormatL( const CSmlDataStoreFormat& /*aServerDataStoreFormat*/ )
+ {
+ TRACE_FUNC;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoSetRemoteMaxObjectSize
+// Not supported
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoSetRemoteMaxObjectSize( TInt /*aServerMaxObjectSize*/ )
+ {
+ TRACE_FUNC;
+ }
+
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoMaxObjectSize
+// Reads the maximum object size from the central repository
+// -----------------------------------------------------------------------------
+TInt CContactsGrpDataStore::DoMaxObjectSize() const
+ {
+ TRACE_FUNC;
+ return 0; // no limit
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoOpenItemL
+// Opens item in the DataStore, reads it (either completely or partially)
+// to the temporary buffer where it can be later read to the remote database.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoOpenItemL(
+ TSmlDbItemUid aUid,
+ TBool& aFieldChange,
+ TInt& aSize,
+ TSmlDbItemUid& aParent,
+ TDes8& aMimeType,
+ TDes8& aMimeVer,
+ TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+ LOGGER_WRITE_1( "aUid: %d", aUid );
+
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ aFieldChange = EFalse;
+ aParent = KErrNotFound;
+
+ delete iDataBuffer;
+ iDataBuffer = NULL;
+
+ if ( !iContactsDb )
+ {
+ User::Leave( KErrNotReady );
+ }
+
+ iDataBuffer = CBufFlat::NewL( KDefaultBufferSize );
+ iReaderPosition = 0;
+
+ CContactItem* item = iContactsDb->ReadContactLC( aUid );
+ if ( item->Type() != KUidContactGroup )
+ {
+ CleanupStack::PopAndDestroy( item );
+ User::Leave( KErrNotFound );
+ }
+
+ iConverter->ImportDbItemL( *static_cast<CContactGroup*>(item) );
+
+ CleanupStack::PopAndDestroy( item );
+
+ iConverter->ExportVCardL( *iDataBuffer );
+ iConverter->ResetL();
+ aSize = iDataBuffer->Size();
+
+ // Set mime type to correct one
+ AssignString( aMimeType, KContactsGrpItemMimeType );
+ AssignString( aMimeVer, KContactsGrpItemMimeVersion );
+
+ User::RequestComplete( iCallerStatus, KErrNone );
+ iCurrentState = EContactGrpOpening;
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCreateItemL
+// Create new item to the message store.
+// Return the id number of the newly created item
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCreateItemL(
+ TSmlDbItemUid& aUid,
+ TInt aSize,
+ TSmlDbItemUid /*aParent*/,
+ const TDesC8& /*aMimeType*/,
+ const TDesC8& /*aMimeVer*/,
+ TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ // Ensure that we're in a proper state
+ if ( iCurrentState != EOpenAndWaiting )
+ {
+ LOGGER_WRITE_1( "Warning: Unexpected current state: %d", iCurrentState );
+ }
+
+ if ( !iContactsDb )
+ {
+ LOGGER_WRITE( "iContactsDb not ready" );
+ User::Leave( KErrNotReady );
+ }
+
+ // Ensure that we've got enough disk space for the item
+ if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aSize, EDriveC ) )
+ {
+ User::RequestComplete( iCallerStatus, KErrDiskFull );
+ LOGGER_WRITE( "SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull." );
+ return;
+ }
+
+ // item uid is updated when groups is saved to db.
+ iCurrentItem = &aUid;
+ // Save current operation-state so we know what operation
+ // is needed to do on DoCommitItemL
+ iCurrentState = EContactGrpCreating;
+
+ delete iDataBuffer;
+ iDataBuffer = NULL;
+ iDataBuffer = CBufFlat::NewL( KDefaultBufferSize );
+ iWriterPosition = 0;
+
+ User::RequestComplete( iCallerStatus, KErrNone );
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoReplaceItemL
+// Begin the replace operation, ensure that the item really exists
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoReplaceItemL(
+ TSmlDbItemUid aUid,
+ TInt aSize,
+ TSmlDbItemUid /*aParent*/,
+ TBool /*aFieldChange*/,
+ TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+ LOGGER_WRITE_1("aUid: %d", aUid);
+
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ if ( !iContactsDb )
+ {
+ LOGGER_WRITE( "iContactsDb not ready" );
+ User::Leave( KErrNotReady );
+ }
+
+ // Ensure that we've got enough disk space for the item
+ if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, aSize, EDriveC ) )
+ {
+ User::RequestComplete( iCallerStatus, KErrDiskFull );
+ LOGGER_WRITE( "SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull." );
+ return;
+ }
+
+ // check that item exist and is Group-type
+ CContactItem* item = iContactsDb->ReadContactLC( aUid );
+ if ( item->Type() != KUidContactGroup )
+ {
+ CleanupStack::PopAndDestroy( item );
+ User::Leave( KErrNotFound );
+ }
+ CleanupStack::PopAndDestroy( item );
+
+ // init databuffer
+ delete iDataBuffer;
+ iDataBuffer = NULL;
+ iDataBuffer = CBufFlat::NewL( KDefaultBufferSize );
+ iWriterPosition = 0;
+
+ // Save current item, so we know what item needs to be replaced
+ iItemToBeReplaced = aUid;
+ // Save current operation-state so we know what operation
+ // is needed to do on DoCommitItemL
+ iCurrentState = EContactGrpUpdating;
+
+ User::RequestComplete( iCallerStatus, KErrNone );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoReadItemL
+// Read specified amount of data from the temporary buffer
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoReadItemL( TDes8& aBuffer )
+ {
+ TRACE_FUNC_ENTRY;
+
+ if ( iCurrentState != EContactGrpOpening || !iDataBuffer )
+ {
+ LOGGER_WRITE_1( "Unexpected state %d", iCurrentState );
+ User::Leave( KErrNotReady );
+ }
+
+ if ( iReaderPosition == KDataBufferNotReady )
+ {
+ LOGGER_WRITE( "No data to read" );
+ User::Leave( KErrEof );
+ }
+
+ // Thiw is how much data there is left in the buffer
+ TInt left = iDataBuffer->Size() - iReaderPosition;
+
+ if ( left > 0 )
+ {
+ // This is how much there's space in the destination buffer
+ TInt destSize = aBuffer.MaxSize();
+
+ // This is how much we can read
+ TInt toRead = destSize < left ? destSize : left;
+
+ // Read the data from the buffer, then update the position
+ iDataBuffer->Read( iReaderPosition, aBuffer, toRead );
+ iReaderPosition += toRead;
+ }
+ else
+ {
+ iReaderPosition = KDataBufferNotReady;
+ LOGGER_WRITE( "No data to read" );
+ User::Leave( KErrEof );
+ }
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoWriteItemL
+// Write specified amount of data to the temporary buffer
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoWriteItemL( const TDesC8& aData )
+ {
+ TRACE_FUNC_ENTRY;
+
+ if ( iCurrentState != EContactGrpCreating && iCurrentState != EContactGrpUpdating )
+ {
+ LOGGER_WRITE_1( "Unexpected current state: %d", iCurrentState );
+ User::Leave( KErrNotReady );
+ }
+
+ // Calculate total size
+ TInt totalSize = aData.Size() + iDataBuffer->Size();
+
+ // Ensure that we've got enough disk space for the item
+ if ( SysUtil::DiskSpaceBelowCriticalLevelL( &iFs, totalSize, EDriveC ) )
+ {
+ User::RequestComplete( iCallerStatus, KErrDiskFull );
+ LOGGER_WRITE("SysUtil::DiskSpaceBelowCriticalLevelL failed with KErrDiskFull.");
+ return;
+ }
+
+ // Add data to buffer
+ iDataBuffer->InsertL( iWriterPosition, aData );
+ iWriterPosition += aData.Size();
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitItemL
+// Commits item from temporary buffer to the message store
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitItemL( TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ // Check that we're in proper state
+ if ( iCurrentState != EContactGrpCreating && iCurrentState != EContactGrpUpdating )
+ {
+ User::RequestComplete( iCallerStatus, KErrNotReady );
+ LOGGER_WRITE_1( "Unexpected current state: %d", iCurrentState );
+ return;
+ }
+
+ if ( iDataBuffer->Size() <= 0 )
+ {
+ User::RequestComplete( iCallerStatus, KErrNotReady );
+ LOGGER_WRITE_1( "Data buffer has no data (%d)", iDataBuffer->Size() );
+ return;
+ }
+
+ if ( !iContactsDb )
+ {
+ LOGGER_WRITE( "iContactsDb not ready" );
+ User::Leave( KErrNotReady );
+ }
+
+ iDataBuffer->Compress();
+ iConverter->ImportVCardL( iDataBuffer->Ptr(0) );
+
+ const CContactIdArray& contactArr = iConverter->ItemsContained();
+
+ if ( iCurrentState == EContactGrpCreating )
+ {
+ *iCurrentItem = CreateNewGroupL( iConverter->GroupLabel(), contactArr );
+ }
+ else
+ {
+ ReplaceGroupL( iItemToBeReplaced, iConverter->GroupLabel(), contactArr);
+ }
+ iConverter->ResetL();
+
+ // Destroy buffer
+ delete iDataBuffer;
+ iDataBuffer = NULL;
+ iWriterPosition = 0;
+
+ // Restore state and signal we're done
+ iCurrentState = EOpenAndWaiting;
+ User::RequestComplete( iCallerStatus, KErrNone );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::CreateNewGroupL
+// Creates new groupd to db.
+// -----------------------------------------------------------------------------
+TContactItemId CContactsGrpDataStore::CreateNewGroupL( const TDesC& aLabel,
+ const CContactIdArray& aContactArray )
+ {
+ TRACE_FUNC_ENTRY;
+ LOGGER_WRITE_1("aLabel: %S", &aLabel );
+
+ // Check for duplicates
+ if ( GroupNameExistsL( aLabel ) )
+ {
+ LOGGER_WRITE("Duplicate group name found, leave KErrAlreadyExists");
+ User::Leave( KErrAlreadyExists );
+ }
+
+ CContactGroup* group =
+ static_cast<CContactGroup*>
+ (iContactsDb->CreateContactGroupLC( aLabel ));
+
+ // Create new contact group
+
+ TContactItemId groupId = group->Id();
+ LOGGER_WRITE_1("new group id: %d", groupId);
+
+ TBool partiallyUpdated(EFalse);
+ TTime lastMod;
+ lastMod.UniversalTime();
+ TInt err(KErrNone);
+ for ( TInt i=0; i<aContactArray.Count(); i++ )
+ {
+ LOGGER_WRITE_1("Add contact: %d", aContactArray[i] );
+ TRAP( err, iContactsDb->AddContactToGroupL( aContactArray[i], groupId ));
+ if ( err )
+ {
+ LOGGER_WRITE_2("Warning: AddContactToGroupL, contact: %d, err: %d", aContactArray[i], err);
+ partiallyUpdated = ETrue;
+ }
+ else
+ {
+ // If succesfully added, update contact timestamp so contact sync sees those
+ // contacts as changed ones.
+ UpdateContactLastMod( aContactArray[i], lastMod );
+ }
+ }
+
+ CleanupStack::PopAndDestroy( group );
+ group = NULL;
+
+ // if item was only partially updated, do not register to snapshot
+ // --> on next sync item is marked as updated
+ if ( !partiallyUpdated )
+ {
+ // reload group
+ group = static_cast<CContactGroup*>
+ (iContactsDb->ReadContactLC( groupId ));
+ // Inform ChangeFinder of new item
+ TSnapshotItem snapshotItem( groupId );
+ snapshotItem.CreateHashL( *group );
+ iChangeFinder->ItemAddedL( snapshotItem );
+
+ CleanupStack::PopAndDestroy( group );
+ }
+
+
+
+ TRACE_FUNC_EXIT;
+ return groupId;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::ReplaceGroupL
+// Replaces existing group on db
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::ReplaceGroupL( TContactItemId aGroupId, const TDesC& aLabel,
+ const CContactIdArray& aContactArray )
+ {
+ TRACE_FUNC_ENTRY;
+ LOGGER_WRITE_1("aGroupId: %d", aGroupId);
+ LOGGER_WRITE_1("aLabel: %S", &aLabel);
+ TBool partiallyUpdated(EFalse);
+ // check that item exist and is Group-type
+ // The LX suffix means that the lock record of the contact item is left on the cleanup stack.
+ CContactItem* item = iContactsDb->OpenContactLX( aGroupId );
+ CleanupStack::PushL( item );
+ if ( item->Type() != KUidContactGroup )
+ {
+ LOGGER_WRITE("Type was not KUidContactGroup, leaving KErrNotFound");
+ User::Leave( KErrNotFound );
+ }
+
+ // cast item to CContactGroup type
+ CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+ TContactItemId groupId = groupItem->Id();
+
+ TBool labelUpdated(EFalse);
+ // Check is group label changed
+ if ( groupItem->HasItemLabelField() )
+ {
+ TPtrC dbLabel = groupItem->GetGroupLabelL();
+ if ( aLabel.Compare( dbLabel ) != 0 )
+ {
+ // didn't match, update label.
+ // Check that new name does not already exist
+ if ( GroupNameExistsL( aLabel ) )
+ {
+ LOGGER_WRITE("Name already exists, leave KErrAlreadyExists");
+ User::Leave( KErrAlreadyExists );
+ }
+ LOGGER_WRITE("Update label" );
+ groupItem->SetGroupLabelL( aLabel );
+ iContactsDb->CommitContactL( *item );
+ labelUpdated = ETrue;
+ }
+ }
+ TTime lastMod;
+ lastMod.UniversalTime();
+ // Update joined contacts
+ TInt err(KErrNone);
+ // add new items to current group
+ for ( TInt i=0; i<aContactArray.Count(); i++ )
+ {
+ TContactItemId newItem = aContactArray[i];
+ TBool founded = groupItem->ContainsItem( newItem );
+ if ( !founded )
+ {
+ // item does not exist on current group, add it.
+ LOGGER_WRITE_1("Add contact: %d", newItem)
+ TRAP( err, iContactsDb->AddContactToGroupL( newItem, groupId ));
+ if ( err )
+ {
+ LOGGER_WRITE_2("Warning: Could not add contact: %d, err: %d", newItem, err);
+ partiallyUpdated = ETrue;
+ }
+ else
+ {
+ // Update contact timestamp
+ UpdateContactLastMod( newItem, lastMod );
+ }
+ }
+ else
+ {
+ LOGGER_WRITE_1("contact( %d ) already exist on group, no need to update", newItem)
+ }
+ }
+
+ // remove all missing items from group
+ CContactIdArray* oldIdArr = groupItem->ItemsContainedLC();
+ if ( oldIdArr )
+ {
+ for ( TInt i=0; i<oldIdArr->Count(); i++ )
+ {
+ TContactItemId oldItem = (*oldIdArr)[i];
+ TInt err = aContactArray.Find( oldItem );
+ if ( err == KErrNotFound )
+ {
+ LOGGER_WRITE_1("Remove contact: %d", oldItem );
+ // old item was not found from new item list, remove it.
+ TRAP( err, iContactsDb->RemoveContactFromGroupL(oldItem, groupId));
+ if ( err )
+ {
+ LOGGER_WRITE_2("Warning: Could not remove contact: %d, err: ",oldItem, err );
+ partiallyUpdated = ETrue;
+ }
+ else
+ {
+ UpdateContactLastMod( oldItem, lastMod );
+ }
+ }
+ // Update all contacts in group if label is changed
+ else if ( labelUpdated )
+ {
+ UpdateContactLastMod( oldItem, lastMod );
+ }
+ }
+ }
+ CleanupStack::PopAndDestroy( oldIdArr );
+ CleanupStack::PopAndDestroy( 2 ); // item, lock object
+
+ // if item was only partially updated, do not register to snapshot
+ // --> on next sync item is marked as updated
+ if ( !partiallyUpdated )
+ {
+ // reload group
+ groupItem = NULL;
+ groupItem = static_cast<CContactGroup*>
+ (iContactsDb->ReadContactLC( groupId ));
+ // Inform ChangeFinder of updated item
+ TSnapshotItem snapshotItem( groupId );
+ snapshotItem.CreateHashL( *groupItem );
+ iChangeFinder->ItemUpdatedL( snapshotItem );
+
+ CleanupStack::PopAndDestroy( groupItem );
+ }
+
+ TRACE_FUNC_EXIT;
+ }
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCloseItem
+// Closes open item in the data store
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCloseItem()
+ {
+ TRACE_FUNC_ENTRY;
+ // Reset read buffer
+ iReaderPosition = KDataBufferNotReady;
+ delete iDataBuffer;
+ iDataBuffer = NULL;
+
+ // Make sure that we're opened an item
+ if ( iCurrentState != EContactGrpOpening )
+ {
+ LOGGER_WRITE_1( "WARNING: Invalid state %d.", iCurrentState );
+ }
+ // Start to wait for the next operation
+ iCurrentState = EOpenAndWaiting;
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoMoveItemL
+// Not supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoMoveItemL( TSmlDbItemUid /*aUid*/,
+ TSmlDbItemUid /*aNewParent*/, TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ // Restore state and signal we're done
+ User::RequestComplete( iCallerStatus, KErrNotSupported );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoDeleteItemL
+// Removes item from the message store
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoDeleteItemL( TSmlDbItemUid aUid, TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+ LOGGER_WRITE_1( "aUid: %d", aUid );
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ // Check that we're in proper state
+ if ( iCurrentState != EOpenAndWaiting )
+ {
+ LOGGER_WRITE_1("CContactsGrpDataStore::DoDeleteItemL, Incorrect state: %d", iCurrentState);
+ }
+
+ if ( !iContactsDb )
+ {
+ LOGGER_WRITE( "iContactsDb not ready" );
+ User::Leave( KErrNotReady );
+ }
+
+ // check that item type is ContactGroup
+ CContactItem* item = iContactsDb->ReadContactLC( aUid );
+ if ( item->Type() != KUidContactGroup )
+ {
+ LOGGER_WRITE("Item was not ContactGroup");
+ CleanupStack::PopAndDestroy( item );
+ User::Leave( KErrNotFound );
+ }
+
+ // Update all contacts in group
+ CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+ CContactIdArray* contactArr = groupItem->ItemsContainedLC();
+ if ( contactArr )
+ {
+ TTime lastMod;
+ lastMod.UniversalTime();
+ for ( TInt i=0; i<contactArr->Count(); i++ )
+ {
+ TContactItemId contactId = (*contactArr)[i];
+ UpdateContactLastMod( contactId, lastMod );
+ }
+ }
+ CleanupStack::PopAndDestroy( contactArr );
+ CleanupStack::PopAndDestroy( item );
+
+ // delete group
+ iContactsDb->DeleteContactL( aUid );
+
+ TSnapshotItem snapshotItem( aUid );
+ iChangeFinder->ItemDeleted( snapshotItem );
+
+ User::RequestComplete( iCallerStatus, KErrNone );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoSoftDeleteItemL
+// Soft delete isn't supported.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoSoftDeleteItemL(TSmlDbItemUid /*aUid*/, TRequestStatus& aStatus)
+ {
+ TRACE_FUNC_ENTRY;
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ User::RequestComplete(iCallerStatus, KErrNotSupported);
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoDeleteAllItemsL
+// Deletes all items in the standard folders of bookmark store
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoDeleteAllItemsL( TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ if ( !iContactsDb )
+ {
+ LOGGER_WRITE( "iContactsDb is not opened, Leaving KErrNotReady");
+ User::Leave( KErrNotReady );
+ }
+
+ CContactIdArray* groups = iContactsDb->GetGroupIdListL();
+ if ( groups )
+ {
+ CleanupStack::PushL( groups );
+ TTime lastMod;
+ lastMod.UniversalTime();
+
+ // update all contacts in all groups
+ for ( TInt i=0; i<groups->Count(); i++ )
+ {
+ // check that item type is ContactGroup
+ CContactItem* item = iContactsDb->ReadContactLC( (*groups)[i] );
+
+ // Update all contacts in group
+ CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+ CContactIdArray* contactArr = groupItem->ItemsContainedLC();
+ if ( contactArr )
+ {
+ for ( TInt i=0; i<contactArr->Count(); i++ )
+ {
+ TContactItemId contactId = (*contactArr)[i];
+ UpdateContactLastMod( contactId, lastMod );
+ }
+ }
+ CleanupStack::PopAndDestroy( contactArr );
+ CleanupStack::PopAndDestroy( item );
+ }
+
+ // delete all groups
+ iContactsDb->DeleteContactsL( *groups );
+ CleanupStack::PopAndDestroy( groups );
+ }
+
+ // Reset the whole change finder
+ iChangeFinder->ResetL();
+
+ User::RequestComplete( iCallerStatus, KErrNone );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoHasSyncHistory
+// This method returns ETrue if Data Store has history information.
+// Slow-sync will be used if Data Store does not have history information.
+// -----------------------------------------------------------------------------
+TBool CContactsGrpDataStore::DoHasSyncHistory() const
+ {
+ TRACE_FUNC_ENTRY;
+ LOGGER_WRITE_1( "iHasHistory: %d", iHasHistory );
+ TRACE_FUNC_EXIT;
+ return iHasHistory;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoAddedItems
+// This method returns UIDs of added items. Those items are added after previous
+// synchronization with current synchronization relationship.
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoAddedItems() const
+ {
+ TRACE_FUNC_ENTRY;
+
+ // Clear new-items array
+ iNewItems->Reset();
+ if ( !iChangeFinder )
+ {
+ LOGGER_WRITE( "WARNING: Not ready, returns empty item list" );
+ return *iNewItems;
+ }
+
+ // Search for new items
+ TRAPD( error, iChangeFinder->FindNewItemsL( *iNewItems ) )
+ if ( error != KErrNone )
+ {
+ LOGGER_WRITE_1( "CContactsGrpDataStore::DoAddedItems, iChangeFinder->FindNewItemsL leaved with %d.", error );
+ }
+
+ LOGGER_WRITE_1( "New item count: %d.", iNewItems->ItemCount() );
+ TRACE_FUNC_EXIT;
+
+ return *iNewItems;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoDeletedItems
+// Returns ids of items, which are deleted after previous synchronization
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoDeletedItems() const
+ {
+ TRACE_FUNC_ENTRY;
+
+ // Clear deleted-items array
+ iDeletedItems->Reset();
+ if ( !iChangeFinder )
+ {
+ LOGGER_WRITE( "WARNING: Not ready, returns empty item list" );
+ return *iDeletedItems;
+ }
+
+ // Search for deleted items
+ TRAPD( error, iChangeFinder->FindDeletedItemsL( *iDeletedItems ) );
+ if ( error != KErrNone )
+ {
+ LOGGER_WRITE_1( "CContactsGrpDataStore::DoDeletedItems, iChangeFinder->FindDeletedItemsL leaved with %d.", error );
+ }
+
+ LOGGER_WRITE_1( "Deleted item count: %d.", iDeletedItems->ItemCount() );
+ TRACE_FUNC_EXIT;
+ return *iDeletedItems;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoSoftDeletedItems
+// Not directly supported, empty list returned
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoSoftDeletedItems() const
+ {
+ TRACE_FUNC;
+ // Return empty array as a result
+ return *iEmptyList;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoModifiedItems
+// Finds all modified items in the data store
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoModifiedItems() const
+ {
+ TRACE_FUNC_ENTRY;
+
+ // Clear updated-items array
+ iUpdatedItems->Reset();
+ if ( !iChangeFinder )
+ {
+ LOGGER_WRITE( "WARNING: Not ready, returns empty item list" );
+ return *iUpdatedItems;
+ }
+
+ // Search for updated items
+ TRAPD( error, iChangeFinder->FindChangedItemsL( *iUpdatedItems ) )
+ if ( error != KErrNone )
+ {
+ LOGGER_WRITE_1( "CContactsGrpDataStore::DoModifiedItems, iChangeFinder->FindChangedItemsL leaved with %d.", error );
+ }
+
+ LOGGER_WRITE_1( "Modified item count: %d.", iUpdatedItems->ItemCount() );
+ TRACE_FUNC_EXIT;
+ return *iUpdatedItems;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoMovedItems
+// Finds all moved items in the data store
+// -----------------------------------------------------------------------------
+const MSmlDataItemUidSet& CContactsGrpDataStore::DoMovedItems() const
+ {
+ TRACE_FUNC;
+ // Moved items are not supported
+ // Return empty array as a result
+ return *iEmptyList;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoResetChangeInfoL
+// Resets change history in the data store. All content is considered
+// new in the data store point of view.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoResetChangeInfoL( TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ // Check that we're in proper state
+ if ( iCurrentState != EOpenAndWaiting )
+ {
+ LOGGER_WRITE_1( "CContactsGrpDataStore::DoResetChangeInfoL, invalid state %d.", iCurrentState );
+ }
+
+ // Reset change info in ChangeFinder
+ iChangeFinder->ResetL();
+ iHasHistory = EFalse;
+
+ // Signal we're done
+ User::RequestComplete( iCallerStatus, KErrNone );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitChangeInfoL
+// Commits change info. These items are no longer reported, when change
+// information is being queried.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitChangeInfoL( TRequestStatus& aStatus,
+ const MSmlDataItemUidSet& aItems )
+ {
+ TRACE_FUNC_ENTRY;
+
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ // Ensure that we're in a proper state
+ if ( iCurrentState != EOpenAndWaiting )
+ {
+ LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, invalid state %d.", iCurrentState );
+ }
+
+ // Notify ChangeFinder
+ LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, item count %d.", aItems.ItemCount() );
+ iChangeFinder->CommitChangesL( aItems );
+ iHasHistory = ETrue;
+
+ // Signal we're done
+ User::RequestComplete( iCallerStatus, KErrNone );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::DoCommitChangeInfoL
+// Commits change info. There is no more nothing to report when change
+// information is being queried.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::DoCommitChangeInfoL( TRequestStatus& aStatus )
+ {
+ TRACE_FUNC_ENTRY;
+
+ iCallerStatus = &aStatus;
+ *iCallerStatus = KRequestPending;
+
+ // Ensure that we're in a proper state
+ if ( iCurrentState != EOpenAndWaiting )
+ {
+ LOGGER_WRITE_1( "CContactsGrpDataStore::DoCommitChangeInfoL, invalid state %d.", iCurrentState );
+ }
+
+ // Notify ChangeFinder
+ iChangeFinder->CommitChangesL();
+ iHasHistory = ETrue;
+
+ // Signal we're done
+ User::RequestComplete( iCallerStatus, KErrNone );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::RegisterSnapshotL
+// Sets Changefinder to compare against current message store content
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::RegisterSnapshotL()
+ {
+ TRACE_FUNC_ENTRY;
+
+ CArrayFixSeg<TSnapshotItem>* snapshot =
+ new ( ELeave ) CArrayFixSeg<TSnapshotItem>( KSnapshotGranularity );
+ CleanupStack::PushL( snapshot );
+
+ // Add everything to snapshot
+
+ // GetGroupIdListL returns NULL if there are no groups in the database.
+ CContactIdArray* groups = iContactsDb->GetGroupIdListL();
+ if ( groups )
+ {
+ TKeyArrayFix key( iKey );
+ CleanupStack::PushL( groups );
+ for ( TInt i=0; i<groups->Count(); i++ )
+ {
+ CContactItem* item = iContactsDb->ReadContactLC((*groups)[i]);
+ CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+
+ LOGGER_WRITE_1( "Add group to snatshot, group id: %d",groupItem->Id() );
+
+ TSnapshotItem snapshotItem( groupItem->Id() );
+ snapshotItem.CreateHashL( *groupItem );
+
+ snapshot->InsertIsqL( snapshotItem, key );
+
+ CleanupStack::PopAndDestroy( item );
+ }
+ CleanupStack::PopAndDestroy( groups );
+ }
+
+ // Set new snapshot to compare against
+ iChangeFinder->SetNewSnapshot( snapshot );
+
+ // Changefinder takes ownership of the snapshot
+ CleanupStack::Pop( snapshot );
+
+ TRACE_FUNC_EXIT;
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::AssignString
+// Assigns data from one descriptor into another, truncates if too long
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::AssignString( TDes8& aDestination, const TDesC8& aSource )
+ {
+ TInt targetLength = aSource.Length();
+ if ( aDestination.MaxLength() < targetLength )
+ {
+ targetLength = aDestination.MaxLength();
+ }
+
+ aDestination.Copy( aSource.Ptr(), targetLength );
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::UpdateContactLastMod
+// Updates contact item last modified time. Contact sync will see item as changed one.
+// -----------------------------------------------------------------------------
+void CContactsGrpDataStore::UpdateContactLastMod( TContactItemId aContactId,
+ const TTime& aLastModified )
+ {
+ // Ignore errors.
+ // Cannot update timestamp if contact is already open. However modified time
+ // is most likely still updated by UI.
+ TRAP_IGNORE(
+ // OpenContactLX() places the edit lock on the cleanup stack
+ CContactItem* contact = iContactsDb->OpenContactLX( aContactId );
+ CleanupStack::PushL( contact );
+ contact->SetLastModified( aLastModified );
+ iContactsDb->CommitContactL( *contact );
+ CleanupStack::PopAndDestroy( contact );
+ CleanupStack::PopAndDestroy( 1 ); // lock object
+ );
+ }
+
+// -----------------------------------------------------------------------------
+// CContactsGrpDataStore::GroupNameExistsL
+// Checks does group name already exists.
+// -----------------------------------------------------------------------------
+TBool CContactsGrpDataStore::GroupNameExistsL( const TDesC& aLabel )
+ {
+ TBool duplicateFound( EFalse );
+ // GetGroupIdListL returns NULL if there are no groups in the database.
+ CContactIdArray* groups = iContactsDb->GetGroupIdListL();
+ if ( groups )
+ {
+ CleanupStack::PushL( groups );
+ for ( TInt i=0; i<groups->Count() && !duplicateFound; i++ )
+ {
+ CContactItem* item = iContactsDb->ReadContactLC((*groups)[i]);
+ CContactGroup* groupItem = static_cast<CContactGroup*>(item);
+
+ if ( groupItem->HasItemLabelField() )
+ {
+ TPtrC label = groupItem->GetGroupLabelL();
+ if ( aLabel.Compare( label ) == 0 )
+ {
+ duplicateFound = ETrue;
+ }
+ }
+ CleanupStack::PopAndDestroy( item );
+ }
+ CleanupStack::PopAndDestroy( groups );
+ }
+
+ return duplicateFound;
+ }