/*
* Copyright (c) 2002 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:
* Implementation helper class for class CPbkContactEngine.
*
*/
// INCLUDE FILES
#include "CPbkContactEngine.h"
#include "CContactDbConnection.h"
#include <cntdb.h>
#include <cntview.h>
#include <cntitem.h>
#include <sysutil.h>
#include <featmgr.h>
#include <shareddataclient.h>
#include <PbkDebug.h>
#include "PbkProfiling.h"
#include "PbkCompressConfig.h"
#include "CPbkDefaultCompressionStrategy.h"
#include "PbkUID.h"
#include "CPbkFieldsInfo.h"
#include "TPbkMatchPriorityLevel.h"
#include "CPbkEngineExtension.h"
#include "CPbkSortOrderManager.h"
#include "PbkNameFormatterFactory.h"
#include <MPbkContactNameFormat.h>
// Unnamed namespace for local definitions
namespace {
// LOCAL CONSTANTS AND MACROS
/// Name of the shared contacts view
_LIT(KPbkAllContactsViewName, "AllContacts");
/// Default contact database name
_LIT(KDefaultDbName, "C:Contacts.cdb");
/// Default contact view preferences
const TContactViewPreferences KPbkDefaultContactViewPrefs =
static_cast<TContactViewPreferences>
( EContactCardsOnly | EUnSortedAtEnd | ESingleWhiteSpaceIsEmptyField );
#ifdef _DEBUG
enum TPanicCode
{
EPanicUninitializedData = 1
};
// LOCAL FUNCTIONS
void Panic(TPanicCode aReason)
{
_LIT(KPanicText, "CContactDbConnection");
User::Panic(KPanicText, aReason);
}
#endif
// ==================== LOCAL FUNCTIONS ====================
/**
* Returns true if aLhs and aRhs differ.
*/
inline TBool operator!=
(const RContactViewSortOrder& aLhs, const RContactViewSortOrder& aRhs)
{
return !(aLhs == aRhs);
}
/**
* Wrapper class for calling default file name or specified filename versions
* of CContactDatabase::ReplaceL, OpenL and CreateL.
*/
NONSHARABLE_CLASS(TDbOpen)
{
public:
/**
* Constructor.
* @param aFileName file name
* @param aReplace open or replace the database
* @param aSharedDataClient shared data client
*/
TDbOpen(const TDesC* aFileName, TBool aReplace,
const RSharedDataClient& aSharedDataClient);
/**
* Opens or replaces database.
*/
CContactDatabase* OpenOrReplaceL() const;
/**
* Creates a new database.
*/
CContactDatabase* CreateL() const;
/**
* Returns the drive the database is stored on.
* @precond OpenOrReplaceL() or CreateL() has been called.
*/
TDriveNumber DbDrive(RFs& aFs) const;
private: // Implementation
CContactDatabase* ReplaceL() const;
CContactDatabase* OpenL() const;
private:
/// Own: file name
TFileName iFileName;
/// Own: open or replace flag
const TBool iReplace;
/// Ref: contact database
mutable CContactDatabase* iDb;
/// Ref: Shared data client
const RSharedDataClient& iSharedDataClient;
};
/**
* Updates system template to reflect aFieldsInfo.
*/
TBool UpdateSystemTemplateFieldsL
(CContactItem& aSysTemplate,
const CPbkFieldsInfo& aFieldsInfo);
} // namespace
// ================= MEMBER FUNCTIONS =======================
inline CPbkContactEngine::CContactDbConnection::CContactDbConnection()
{
}
inline TBool CPbkContactEngine::CContactDbConnection::CheckSystemTemplateL
(const CPbkFieldsInfo& aFieldsInfo)
{
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::CheckSystemTemplateL(0x%x)"),this);
// Open the system template
TRAPD(err, iSysTemplate = iContactDb->OpenContactL(
iContactDb->TemplateId()));
switch (err)
{
case KErrNone:
{
// All is well
break;
}
case KErrInUse:
{
// Assume some other CPbkContactEngine instance is currently
// performing this check -> skip the test. The test could be
// reattempted after a delay but that would slow down the
// initialisation of this class even more.
return EFalse;
}
default:
{
// Propagate other errors to caller
User::Leave(err);
return EFalse;
}
}
TBool result = EFalse;
if (UpdateSystemTemplateFieldsL(*iSysTemplate,aFieldsInfo))
{
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::CheckSystemTemplateL(0x%x): updating system template...."),
this);
iContactDb->CommitContactL(*iSysTemplate);
result = ETrue;
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::CheckSystemTemplateL(0x%x): system template updated."), this);
}
CloseSystemTemplate();
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::CheckSystemTemplateL(0x%x) succesful"),this);
return result;
}
inline void CPbkContactEngine::CContactDbConnection::CreateAllContactsViewL
(const RContactViewSortOrder& aSortOrder)
{
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::CreateAllContactsViewL(0x%x)"),this);
__PBK_PROFILE_RESET(PbkProfiling::EAllContactsViewOpen);
__PBK_PROFILE_START(PbkProfiling::EAllContactsViewOpen);
iAllContactsView = CContactNamedRemoteView::NewL
(*this,
KPbkAllContactsViewName,
Database(),
aSortOrder,
KPbkDefaultContactViewPrefs);
InitViewL(*iAllContactsView);
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::CreateAllContactsViewL(0x%x) succesful"),this);
}
inline void CPbkContactEngine::CContactDbConnection::ConstructL
(const TParams& aParams, TBool aSettingsVisible)
{
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::ConstructL(0x%x)"),this);
iFs = aParams.iFs;
__PBK_PROFILE_START(PbkProfiling::EContactDbOpen);
// Shared data client is created and connected in CPbkContactEngine
__ASSERT_DEBUG(aParams.iEngine && aParams.iEngine->iSharedDataClient,
Panic(EPanicUninitializedData));
TDbOpen dbOpen(aParams.iDbFileName, aParams.iReplaceExistingDb,
*aParams.iEngine->iSharedDataClient);
TRAPD(error, iContactDb = dbOpen.OpenOrReplaceL());
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::ConstructL(0x%x): CContactDatabase open result=%d."),this, error);
if (error == KErrNotFound)
{
// Db not found, create it
TRAP(error, iContactDb = dbOpen.CreateL());
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::ConstructL(0x%x): CContactDatabase create result=%d."),this, error);
}
__PBK_PROFILE_END(PbkProfiling::EContactDbOpen);
*aParams.iDbOpenError = error;
if (error != KErrNone)
{
User::Leave(error);
}
// Store database drive
iDbDrive = dbOpen.DbDrive(iFs);
// Call engine extension plugins
aParams.iExtension->AddFieldTypesL(*aParams.iFieldsInfo);
aParams.iExtension->ModifyFieldTypesL(*aParams.iFieldsInfo);
// Check and update system template if needed
TBool updated = EFalse;
TRAP(error, updated = CheckSystemTemplateL(*aParams.iFieldsInfo));
*aParams.iDbOpenError = error;
if (error != KErrNone)
{
User::Leave(error);
}
if (updated)
{
// close and reopen contact database if system template has been updated
delete iContactDb;
iContactDb = NULL;
iContactDb = dbOpen.OpenOrReplaceL();
}
// Create contact DB change notifier and connect it to aObserver
iContactChangeNotifier = CContactChangeNotifier::NewL
(*iContactDb, aParams.iContactDbObserver);
// Create DB compressor
iCompressionStrategy = CPbkDefaultCompressionStrategy::NewL
(*iContactDb, iDbDrive, iFs);
// Create contact Sort order managing object
iSortOrderManager = CPbkSortOrderManager::NewL(iFs, aSettingsVisible);
// Create the shared all contacts view
CreateAllContactsViewL(iSortOrderManager->SortOrder());
// Set view to sort order manager
iSortOrderManager->SetContactView(*iAllContactsView);
// create name formatting object
iContactNameFormatter = PbkNameFormatterFactory::CreateL
(*aParams.iUnnamedTitle, *iSortOrderManager, aSettingsVisible);
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::ConstructL(0x%x) succesful."),this);
}
CPbkContactEngine::CContactDbConnection* CPbkContactEngine::CContactDbConnection::NewL
(const TParams& aParams, TBool aSettingsVisible)
{
PBK_DEBUG_PRINT(PBK_DEBUG_STRING("CContactDbConnection::NewL()"));
CContactDbConnection* self = new(ELeave) CContactDbConnection;
CleanupStack::PushL(self);
self->ConstructL(aParams, aSettingsVisible);
CleanupStack::Pop(self);
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::NewL() succesful, result=0x%x"), self);
return self;
}
CPbkContactEngine::CContactDbConnection::~CContactDbConnection()
{
for (TInt i = iFilteredViews.Count()-1; i >= 0; --i)
{
iFilteredViews[i].iFilteredView->Close(*this);
}
iFilteredViews.Close();
if (iGroupsLocalView)
{
iGroupsLocalView->Close(*this);
}
if (iAllContactsView)
{
iAllContactsView->Close(*this);
}
if (iCompressionStrategy)
{
iCompressionStrategy->Release();
}
delete iContactChangeNotifier;
delete iContactNameFormatter;
delete iSortOrderManager;
CloseSystemTemplate();
delete iContactDb;
}
CContactViewBase& CPbkContactEngine::CContactDbConnection::AllGroupsViewL()
{
// start loading the all the groups
if (!iGroupsLocalView)
{
// Create a CContactLocalView that contains only groups.
RContactViewSortOrder groupViewSortOrder;
CleanupClosePushL(groupViewSortOrder);
// KUidContactFieldTemplateLabel contains the group label
groupViewSortOrder.AppendL(KUidContactFieldTemplateLabel);
iGroupsLocalView = CContactLocalView::NewL(
*this, *iContactDb, groupViewSortOrder, EGroupsOnly);
InitViewL(*iGroupsLocalView);
CleanupStack::PopAndDestroy(); // groupViewSortOrder
}
return *iGroupsLocalView;
}
CContactViewBase& CPbkContactEngine::CContactDbConnection::FilteredContactsViewL
(TInt aFilter)
{
const TInt count = iFilteredViews.Count();
for (TInt i=0; i < count; ++i)
{
if (iFilteredViews[i].iFilter == aFilter)
{
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"Returned filtered view filter=%x from the cache"), aFilter);
return *iFilteredViews[i].iFilteredView;
}
}
// Ending this profile is in HandleContactViewEvent
__PBK_PROFILE_RESET(PbkProfiling::EFilteredViewOpen);
__PBK_PROFILE_START(PbkProfiling::EFilteredViewOpen);
TFilteredView filter;
filter.iFilter = aFilter;
filter.iFilteredView = CContactFilteredView::NewL
(*this, *iContactDb, *iAllContactsView, aFilter);
InitViewL(*filter.iFilteredView);
const TInt err = iFilteredViews.Append(filter);
if (err != KErrNone)
{
filter.iFilteredView->Close(*this);
User::Leave(err);
}
return *filter.iFilteredView;
}
void CPbkContactEngine::CContactDbConnection::SetCompressUi
(MPbkCompressUi* aCompressUi)
{
iCompressionStrategy->SetCompressUi(aCompressUi);
}
TBool CPbkContactEngine::CContactDbConnection::IsCompressionEnabled() const
{
return iCompressionStrategy->IsCompressionEnabled();
}
TBool CPbkContactEngine::CContactDbConnection::CheckCompress()
{
return iCompressionStrategy->CheckCompress();
}
void CPbkContactEngine::CContactDbConnection::CompressL()
{
iCompressionStrategy->CompressL();
}
void CPbkContactEngine::CContactDbConnection::CancelCompress() const
{
iCompressionStrategy->CancelCompress();
}
void CPbkContactEngine::CContactDbConnection::CheckFileSystemSpaceAndCompressL()
{
PBK_COMPRESS_LOG
(PBK_COMPRESS_STRING(
"CContactDbConnection::CheckFileSystemSpaceAndCompressL(0x%x), DB size = %d bytes"),
this, Database().FileSize());
// Cancel any async compress in progress
CancelCompress();
// Compress synchronously always if compression is required by the DB
// or file system space is below critical level
if (Database().CompressRequired() || FileSpaceLowOnDbDrive())
{
TRAPD(result, Database().CompactL());
PBK_COMPRESS_LOG(PBK_COMPRESS_STRING(" Compacted database, result=%d"), result);
}
// If file system space is still under critical level leave with
// error code KErrDiskFull.
if (FileSpaceLowOnDbDrive())
{
PBK_COMPRESS_LOG
(PBK_COMPRESS_STRING(" FFS space is under critical level -> leaving with KErrDiskFull(%d)"),
KErrDiskFull);
User::Leave(KErrDiskFull);
}
}
TBool CPbkContactEngine::CContactDbConnection::FileSpaceLowOnDbDrive() const
{
TBool result = EFalse;
TRAP_IGNORE(result = SysUtil::DiskSpaceBelowCriticalLevelL(&iFs,0,iDbDrive));
if (result)
{
PBK_COMPRESS_LOG(PBK_COMPRESS_STRING("CContactDbConnection::FileSpaceLowOnDbDrive() returning ETrue"));
}
return result;
}
void CPbkContactEngine::CContactDbConnection::HandleContactViewEvent
#ifdef PBK_ENABLE_PROFILE
(const CContactViewBase& aView, const TContactViewEvent& aEvent)
#else
(const CContactViewBase& /*aView*/, const TContactViewEvent& /*aEvent*/)
#endif
{
#ifdef PBK_ENABLE_PROFILE
if (aEvent.iEventType != TContactViewEvent::EReady)
{
return;
}
if (&aView == iAllContactsView)
{
__PBK_PROFILE_END(PbkProfiling::EAllContactsViewOpen);
__PBK_PROFILE_DISPLAY(PbkProfiling::EAllContactsViewOpen);
__PBK_PROFILE_RESET(PbkProfiling::EAllContactsViewOpen);
}
else if (iFilteredViews.Count() > 0)
{
for (TInt i=0; i < iFilteredViews.Count(); ++i)
{
if (iFilteredViews[i].iFilteredView == &aView)
{
__PBK_PROFILE_END(PbkProfiling::EFilteredViewOpen);
__PBK_PROFILE_DISPLAY(PbkProfiling::EFilteredViewOpen);
__PBK_PROFILE_RESET(PbkProfiling::EFilteredViewOpen);
}
}
}
#endif // PBK_ENABLE_PROFILE
}
void CPbkContactEngine::CContactDbConnection::CloseSystemTemplate()
{
if (iSysTemplate)
{
// Contact Model documentation says CloseContactL cannot leave
iContactDb->CloseContactL(iSysTemplate->Id());
delete iSysTemplate;
iSysTemplate = NULL;
}
}
void CPbkContactEngine::CContactDbConnection::ChangeSortOrderL
(const RContactViewSortOrder& aSortOrder) const
{
// Compare is the new sort order same than the previous one
if (iAllContactsView->SortOrderL() != aSortOrder)
{
// It wasn't, change the sort order
iAllContactsView->ChangeSortOrderL(aSortOrder);
}
}
MPbkContactNameFormat& CPbkContactEngine::CContactDbConnection::ContactNameFormatter() const
{
return *iContactNameFormatter;
}
void CPbkContactEngine::CContactDbConnection::SetNameDisplayOrderL
(TPbkNameOrder aNameOrder)
{
iSortOrderManager->SetNameDisplayOrderL(aNameOrder);
}
CPbkContactEngine::TPbkNameOrder CPbkContactEngine::CContactDbConnection::NameDisplayOrder()
{
return iSortOrderManager->NameDisplayOrder();
}
void CPbkContactEngine::CContactDbConnection::SetNameSeparatorL(TChar aNameSeparator)
{
iSortOrderManager->SetNameSeparatorL(aNameSeparator);
}
TChar CPbkContactEngine::CContactDbConnection::NameSeparator()
{
return iSortOrderManager->NameSeparator();
}
/**
* Call this method for all views created in this class.
*/
void CPbkContactEngine::CContactDbConnection::InitViewL(CContactViewBase& aView)
{
// Set find plugin
aView.SetViewFindConfigPlugin(TUid::Uid(KFindPluginUID));
}
namespace {
const TInt KDiskSpaceForDbOpening = 140*1024; // Space for db opening
// ==================== LOCAL FUNCTIONS ====================
/**
* Returns the default database name using CContactDatabase::GetDefaultNameL().
* If GetDefaultNameL() fails returns silently KDefaultDbName.
*/
void GetDefaultDatabaseName(TDes& aName)
{
TRAPD(err, CContactDatabase::GetDefaultNameL(aName));
if (err != KErrNone)
{
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDatabase::GetDefaultNameL() failed with error code %d"),
err);
aName.Copy(KDefaultDbName);
}
}
TDbOpen::TDbOpen(const TDesC* aFileName, TBool aReplace,
const RSharedDataClient& aSharedDataClient):
iReplace(aReplace), iSharedDataClient(aSharedDataClient)
{
if (aFileName)
{
iFileName.Copy(aFileName->Left(iFileName.MaxLength()));
}
else
{
GetDefaultDatabaseName(iFileName);
}
}
inline CContactDatabase* TDbOpen::ReplaceL() const
{
iDb = CContactDatabase::ReplaceL(iFileName);
return iDb;
}
inline CContactDatabase* TDbOpen::OpenL() const
{
TRAPD(err,iDb = CContactDatabase::OpenL(iFileName));
//Handle KErrDiskFull case separately
if(KErrDiskFull==err)
{
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::TDbOpen::OpenL(0x%x) KErrDiskFull"),this);
// Try to release space for contact db opening
iSharedDataClient.RequestFreeDiskSpaceLC(KDiskSpaceForDbOpening);
iDb = CContactDatabase::OpenL(iFileName);
CleanupStack::PopAndDestroy(); // RequestFreeDiskSpaceLC
PBK_DEBUG_PRINT(PBK_DEBUG_STRING(
"CContactDbConnection::TDbOpen::OpenL(0x%x) ContactDb open"),this);
}
else
{
User::LeaveIfError(err);
}
return iDb;
}
CContactDatabase* TDbOpen::OpenOrReplaceL() const
{
return (iReplace ? ReplaceL() : OpenL());
}
CContactDatabase* TDbOpen::CreateL() const
{
iDb = CContactDatabase::CreateL(iFileName);
return iDb;
}
TDriveNumber TDbOpen::DbDrive(RFs& aFs) const
{
TInt result = EDriveC;
TParsePtrC fileParser(iFileName);
if (fileParser.DrivePresent())
{
const TChar driveLetter(fileParser.Drive()[0]);
TInt drive;
if (aFs.CharToDrive(driveLetter,drive) == KErrNone)
{
result = drive;
}
}
else
{
TDriveUnit driveUnit;
//Note: this function can leave
TRAPD(err, iDb->DatabaseDrive(driveUnit));
if (err == KErrNone)
{
result = driveUnit;
}
}
return static_cast<TDriveNumber>(result);
}
/**
* Updates a single field to the contact database system template. If the field
* does not exist in the system template it is added. If the field exists but
* its label differs from the field type label the system template label is
* updated.
*
* @param aSysTemplate the system template.
* @param aFieldInfo the field type to update.
* @return true if the system template was updated.
*/
TBool UpdateSystemTemplateFieldL
(CContactItem& aSysTemplate, const CPbkFieldInfo& aFieldInfo)
{
TBool updated = EFalse;
// Scan system template field set for the field
CContactItemFieldSet& fieldSet = aSysTemplate.CardFields();
TInt i;
const TInt fieldCount = fieldSet.Count();
for (i = 0; i < fieldCount; ++i)
{
CContactItemField& sysTemplateField = fieldSet[i];
if (aFieldInfo.IsEqualType(sysTemplateField))
{
// Field was found, just check the label
if (!aFieldInfo.IsEqualLabel(sysTemplateField))
{
sysTemplateField.SetLabelL(aFieldInfo.FieldName());
updated = ETrue;
}
break;
}
}
if (i == fieldCount)
{
// Field was not found, add it
CContactItemField* field = aFieldInfo.CreateFieldL();
CleanupStack::PushL(field);
fieldSet.AddL(*field);
CleanupStack::Pop(field);
updated = ETrue;
}
return updated;
}
TBool UpdateSystemTemplateFieldsL
(CContactItem& aSysTemplate,
const CPbkFieldsInfo& aFieldsInfo)
{
TBool updated = EFalse;
const TInt fieldInfoCount = aFieldsInfo.Count();
// Update fields
for (TInt i=0; i < fieldInfoCount; ++i)
{
if (UpdateSystemTemplateFieldL(aSysTemplate, *aFieldsInfo[i]))
{
updated = ETrue;
}
}
return updated;
}
} // namespace
// End of File