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