diff -r cfcbf08528c4 -r 2b40d63a9c3d qtmobility/plugins/contacts/symbian/src/cntsymbianengine.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/qtmobility/plugins/contacts/symbian/src/cntsymbianengine.cpp Fri Apr 16 15:51:22 2010 +0300 @@ -0,0 +1,1211 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include +#include +#include +#include + +#include + +#include + +#include "cntsymbianengine.h" +#include "qcontactchangeset.h" +#include "cntsymbiandatabase.h" +#include "cnttransformcontact.h" +#include "cntsymbiantransformerror.h" +#include "cntsymbianfilterdbms.h" +#include "cntsymbianfiltersql.h" +#include "cntsymbiansorterdbms.h" +#include "cntrelationship.h" +#include "cntdisplaylabel.h" + +typedef QList QContactLocalIdList; +typedef QPair QOwnCardPair; + +// NOTE: There is a bug with RVCT compiler which causes the local stack +// variable to corrupt if the called function leaves. As a workaround we are +// reserving the objects from heap so it will not get corrupted. +// This of course applies only to those stack variables which are passed to +// the called function or the return value of the function is placed to the +// variable + +/* ... The macros changed names */ +#if QT_VERSION < QT_VERSION_CHECK(4, 6, 0) +#define QT_TRAP_THROWING QT_TRANSLATE_SYMBIAN_LEAVE_TO_EXCEPTION +#define QT_TRYCATCH_LEAVING QT_TRANSLATE_EXCEPTION_TO_SYMBIAN_LEAVE +#endif + +CntSymbianEngine::CntSymbianEngine(const QMap& parameters, QContactManager::Error& error) +{ + error = QContactManager::NoError; + + m_dataBase = new CntSymbianDatabase(this, error); + + //Database opened successfully + if(error == QContactManager::NoError) { + m_managerUri = QContactManager::buildUri(CNT_SYMBIAN_MANAGER_NAME, parameters); + m_transformContact = new CntTransformContact; +#ifdef SYMBIAN_BACKEND_USE_SQLITE + m_contactFilter = new CntSymbianFilter(*this, *m_dataBase->contactDatabase(), *m_transformContact); +#else + m_contactFilter = new CntSymbianFilter(*m_dataBase->contactDatabase()); +#endif + m_contactSorter = new CntSymbianSorterDbms(*m_dataBase->contactDatabase(), *m_transformContact); + m_relationship = new CntRelationship(m_dataBase->contactDatabase(), m_managerUri); + m_displayLabel = new CntDisplayLabel(); + } +} + +CntSymbianEngine::CntSymbianEngine(const CntSymbianEngine& other) + : QContactManagerEngine(), + m_dataBase(other.m_dataBase), + m_managerUri(other.m_managerUri), + m_transformContact(other.m_transformContact), + m_contactFilter(other.m_contactFilter), + m_contactSorter(other.m_contactSorter), + m_relationship(other.m_relationship), + m_displayLabel(other.m_displayLabel) +{ +} + +CntSymbianEngine::~CntSymbianEngine() +{ + delete m_contactFilter; // needs to be deleted before database + delete m_dataBase; + delete m_transformContact; + delete m_contactSorter; + delete m_relationship; + delete m_displayLabel; +} + +void CntSymbianEngine::deref() +{ + delete this; +} + +/*! + * Returns a list of the ids of contacts that match the supplied \a filter, sorted according to the given \a sortOrders. + * Any error that occurs will be stored in \a error. Uses either the Symbian backend native filtering or in case of an + * unsupported filter, the generic (slow) filtering of QContactManagerEngine. + */ +QList CntSymbianEngine::contactIds( + const QContactFilter& filter, + const QList& sortOrders, + QContactManager::Error& error) const +{ + error = QContactManager::NoError; + QList result; + + + if (filter.type() == QContactFilter::RelationshipFilter) + { + QContactRelationshipFilter rf = static_cast(filter); + QList relationshipsList = relationships( + rf.relationshipType(), rf.relatedContactId(), rf.relatedContactRole(), error); + if(error == QContactManager::NoError) { + foreach(QContactRelationship r, relationshipsList) { + if(rf.relatedContactRole() == QContactRelationshipFilter::First) { + result += r.second().localId(); + } else if (rf.relatedContactRole() == QContactRelationshipFilter::Second) { + result += r.first().localId(); + } else if (rf.relatedContactRole() == QContactRelationshipFilter::Either) { + result += r.first().localId(); + result += r.second().localId(); + } + } + } + } + else + { + bool filterSupported(true); + result = m_contactFilter->contacts(filter, sortOrders, filterSupported, error); + + //slow sorting until it's supported in SQL requests + result = slowSort(result, sortOrders, error); + +#ifdef SYMBIAN_BACKEND_USE_SQLITE + + // Remove possible false positives + if(!filterSupported && error == QContactManager::NotSupportedError) + result = slowFilter(filter, result, error); + +#else + // Remove possible false positives + if(!filterSupported && error == QContactManager::NoError) + result = slowFilter(filter, result, error); + + // Sort the matching contacts + if(!sortOrders.isEmpty()&& error == QContactManager::NoError ) { + if(m_contactSorter->sortOrderSupported(sortOrders)) { + result = m_contactSorter->sort(result, sortOrders, error); + } else { + result = slowSort(result, sortOrders, error); + } + } +#endif + } + return result; +} + +QList CntSymbianEngine::contactIds(const QList& sortOrders, QContactManager::Error& error) const +{ + // Check if sorting is supported by backend + if(m_contactSorter->sortOrderSupported(sortOrders)) + return m_contactSorter->contacts(sortOrders,error); + + // Backend does not support this sorting. + // Fall back to slow QContact-level sorting method. + + // Get unsorted contact ids + QList noSortOrders; + QList unsortedIds = m_contactSorter->contacts(noSortOrders, error); + if (error != QContactManager::NoError) + return QList(); + + // Sort contacts + return slowSort(unsortedIds, sortOrders, error); +} + +QList CntSymbianEngine::contacts(const QList& sortOrders, const QStringList& definitionRestrictions, QContactManager::Error& error) const +{ + error = QContactManager::NoError; + QList contacts; + QList contactIds = this->contactIds(sortOrders, error); + if (error == QContactManager::NoError ) { + foreach (QContactLocalId id, contactIds) { + QContact contact = this->contact(id, definitionRestrictions, error); + if (error != QContactManager::NoError) { + return QList(); // return empty list if error occurred + } + contacts.append(contact); + } + } + return contacts; +} + +QList CntSymbianEngine::contacts(const QContactFilter& filter, const QList& sortOrders, const QStringList& definitionRestrictions, QContactManager::Error& error) const +{ + error = QContactManager::NoError; + QList contacts; + QList contactIds = this->contactIds(filter, sortOrders, error); + if (error == QContactManager::NoError ) { + foreach (QContactLocalId id, contactIds) { + QContact contact = this->contact(id, definitionRestrictions, error); + if (error != QContactManager::NoError) { + return QList(); // return empty list if error occurred + } + contacts.append(contact); + } + } + return contacts; +} + +/*! + * Read a contact from the contact database. + * + * \param contactId The Id of the contact to be retrieved. + * \param error Qt error code. + * \return A QContact for the requested QContactLocalId value or 0 if the read + * operation was unsuccessful (e.g. contact not found). + */ +QContact CntSymbianEngine::contact(const QContactLocalId& contactId, const QStringList& definitionRestrictions, QContactManager::Error& error) const +{ + QContact* contact = new QContact(); + TRAPD(err, *contact = fetchContactL(contactId, definitionRestrictions)); + CntSymbianTransformError::transformError(err, error); + + if(error == QContactManager::NoError) { + updateDisplayLabel(*contact); + //check relationship only if there are no definition restrictions, otherwise + //skip this time expensive operation. + if( definitionRestrictions.isEmpty()) { + QContactManager::Error relationshipError; + QList relationships = this->relationships(QString(), contact->id(), QContactRelationshipFilter::Either, relationshipError); + if (relationshipError != QContactManager::NoError && + relationshipError != QContactManager::DoesNotExistError) { // means that no relationships found + error = relationshipError; + } + QContactManagerEngine::setContactRelationships(contact, relationships); + } + } + return *QScopedPointer(contact); +} + +bool CntSymbianEngine::saveContact(QContact* contact, QContactManager::Error& error) +{ + QContactChangeSet changeSet; + TBool ret = doSaveContact(contact, changeSet, error); + changeSet.emitSignals(this); + return ret; +} + +/*! \reimp */ +bool CntSymbianEngine::saveContacts(QList *contacts, QMap *errorMap, QContactManager::Error& error) +{ + error = QContactManager::NoError; + + if (errorMap) { + // if the errormap argument is null, we just don't do fine-grained reporting. + errorMap->clear(); + } + + if (!contacts) { + error = QContactManager::BadArgumentError; + return false; + } + + QContactChangeSet changeSet; + for (int i = 0; i < contacts->count(); i++) { + QContact current = contacts->at(i); + QContactManager::Error functionError = QContactManager::NoError; + if (!doSaveContact(¤t, changeSet, functionError)) { + error = functionError; + if (errorMap) { + errorMap->insert(i, functionError); + } + } else { + (*contacts)[i] = current; + } + } + changeSet.emitSignals(this); + return (error == QContactManager::NoError); +} + +/*! + * Uses the generic filtering implementation of QContactManagerEngine to filter + * contacts one-by-one. Really slow when filtering a lot of contacts because + * every contact needs to be loaded from the database before filtering. + */ +QList CntSymbianEngine::slowFilter( + const QContactFilter& filter, + const QList& contacts, + QContactManager::Error& error + ) const +{ + QList result; + for (int i(0); i < contacts.count(); i++) { + QContactLocalId id = contacts.at(i); + + // Check if this is a false positive. If not, add to the result set. + if(QContactManagerEngine::testFilter(filter, contact(id, QStringList(), error))) + result << id; + } + return result; +} + +QList CntSymbianEngine::slowSort( + const QList& contactIds, + const QList& sortOrders, + QContactManager::Error& error) const +{ + // Get unsorted contacts + QList unsortedContacts; + foreach (QContactLocalId id, contactIds) { + QContact c = contact(id, QStringList(), error); + if (error != QContactManager::NoError) + return QList(); + unsortedContacts << c; + } + return QContactManagerEngine::sortContacts(unsortedContacts, sortOrders); +} + +bool CntSymbianEngine::doSaveContact(QContact* contact, QContactChangeSet& changeSet, QContactManager::Error& error) +{ + bool ret = false; + if(contact && !validateContact(*contact, error)) + return false; + + // If contact has GUid and no local Id, try to find it in database + if (contact && !contact->localId() && + contact->details(QContactGuid::DefinitionName).count() > 0) { + QContactDetailFilter guidFilter; + guidFilter.setDetailDefinitionName(QContactGuid::DefinitionName, QContactGuid::FieldGuid); + QContactGuid guidDetail = static_cast(contact->details(QContactGuid::DefinitionName).at(0)); + guidFilter.setValue(guidDetail.guid()); + + QContactManager::Error err; + QList localIdList = contactIds(guidFilter, + QList(), err); + if (err == QContactManager::NoError && localIdList.count() > 0) { + QScopedPointer contactId(new QContactId()); + contactId->setLocalId(localIdList.at(0)); + contactId->setManagerUri(m_managerUri); + contact->setId(*contactId); + } + } + + // Check parameters + if(!contact) { + error = QContactManager::BadArgumentError; + ret = false; + // Update an existing contact + } else if(contact->localId()) { + if(contact->id().managerUri() == m_managerUri) { + ret = updateContact(*contact, changeSet, error); + } else { + error = QContactManager::BadArgumentError; + ret = false; + } + // Create new contact + } else { + ret = addContact(*contact, changeSet, error); + } + + if (ret) + updateDisplayLabel(*contact); + + return ret; +} + +/*! + * Private leaving implementation for contact() + */ +QContact CntSymbianEngine::fetchContactL(const QContactLocalId &localId, const QStringList& definitionRestrictions) const +{ + // A contact with a zero id is not expected to exist. + // Symbian contact database uses id 0 internally as the id of the + // system template. + if(localId == 0) + User::Leave(KErrNotFound); + + // Read the contact from the CContactDatabase + CContactItem* contactItem = m_dataBase->contactDatabase()->ReadContactL(localId); + CleanupStack::PushL(contactItem); + + // Convert to a QContact + QContact contact = m_transformContact->transformContactL(*contactItem, definitionRestrictions); + + // Transform details that are not available until the contact has been saved + m_transformContact->transformPostSaveDetailsL(*contactItem, contact, *m_dataBase->contactDatabase(), m_managerUri); + + CleanupStack::PopAndDestroy(contactItem); + + return contact; +} + +/*! + * Add the specified contact item to the persistent contacts store. + * + * \param contact The QContact to be saved. + * \param id The Id of new contact + * \param qtError Qt error code. + * \return Error status + */ +bool CntSymbianEngine::addContact(QContact& contact, QContactChangeSet& changeSet, QContactManager::Error& qtError) +{ + // Attempt to persist contact, trapping errors + int err(0); + QContactLocalId id(0); + TRAP(err, id = addContactL(contact)); + if(err == KErrNone) + { + changeSet.addedContacts().insert(id); + m_dataBase->appendContactEmitted(id); + } + CntSymbianTransformError::transformError(err, qtError); + + return (err==KErrNone); +} + +/*! + * Private leaving implementation for addContact() + * + * \param contact The contact item to save in the database. + * \return The new contact ID. + */ +int CntSymbianEngine::addContactL(QContact &contact) +{ + int id(0); + + //handle normal contact + if(contact.type() == QContactType::TypeContact) + { + // Create a new contact card. + CContactItem* contactItem = CContactCard::NewLC(); + m_transformContact->transformContactL(contact, *contactItem); + // Add to the database + id = m_dataBase->contactDatabase()->AddNewContactL(*contactItem); + // Reload contact item + CleanupStack::PopAndDestroy(contactItem); + contactItem = 0; + contactItem = m_dataBase->contactDatabase()->ReadContactLC(id); + // Transform details that are not available until the contact has been saved + m_transformContact->transformPostSaveDetailsL(*contactItem, contact, *m_dataBase->contactDatabase(), m_managerUri); + CleanupStack::PopAndDestroy(contactItem); + } + //group contact + else if(contact.type() == QContactType::TypeGroup) + { + // Create a new group, which is added to the database + CContactItem* contactItem = m_dataBase->contactDatabase()->CreateContactGroupLC(); + + //set the id for the contact, needed by update + id = contactItem->Id(); + QScopedPointer contactId(new QContactId()); + contactId->setLocalId(QContactLocalId(id)); + contactId->setManagerUri(m_managerUri); + contact.setId(*contactId); + + //update contact, will add the fields to the already saved group + updateContactL(contact); + // Transform details that are not available until the contact has been saved + m_transformContact->transformPostSaveDetailsL(*contactItem, contact, *m_dataBase->contactDatabase(), m_managerUri); + + CleanupStack::PopAndDestroy(contactItem); + } + // Leave with an error + else + { + User::Leave(KErrInvalidContactDetail); + } + + // Return the new ID. + return id; +} + +/*! + * Update an existing contact entry in the database. + * + * \param contact The contact to update in the database. + * \param qtError Qt error code. + * \return Error status. + */ +bool CntSymbianEngine::updateContact(QContact& contact, QContactChangeSet& changeSet, QContactManager::Error& qtError) +{ + int err(0); + TRAP(err, updateContactL(contact)); + if(err == KErrNone) + { + //TODO: check what to do with groupsChanged + changeSet.changedContacts().insert(contact.localId()); + m_dataBase->appendContactEmitted(contact.localId()); + } + CntSymbianTransformError::transformError(err, qtError); + return (err==KErrNone); +} + +/*! + * Private leaving implementation for updateContact() + * + * \param contact The contact to update in the database. + */ +void CntSymbianEngine::updateContactL(QContact &contact) +{ + // Need to open the contact for write, leaving this item + // on the cleanup stack to unlock the item in the event of a leave. + CContactItem* contactItem = m_dataBase->contactDatabase()->OpenContactLX(contact.localId()); + CleanupStack::PushL(contactItem); + + // Cannot update contact type. The client needs to do this itself. + if ((contact.type() == QContactType::TypeContact && contactItem->Type() != KUidContactCard && + contactItem->Type() != KUidContactOwnCard) || + (contact.type() == QContactType::TypeGroup && contactItem->Type() != KUidContactGroup)){ + User::Leave(KErrAlreadyExists); + } + + // Copy the data from QContact to CContactItem + m_transformContact->transformContactL(contact, *contactItem); + + // Write the entry using the converted contact + // note commitContactL removes empty fields from the contact + m_dataBase->contactDatabase()->CommitContactL(*contactItem); + + updateDisplayLabel(contact); + + CleanupStack::PopAndDestroy(contactItem); + CleanupStack::PopAndDestroy(1); // commit lock +} + +/*! + * Remove the specified contact object from the database. + * + * The removal of contacts from the underlying contacts model database + * is performed in transactions of maximum 50 items at a time. E.g. + * deleting 177 contacts would be done in 3 transactions of 50 and a + * final transaction of 27. + * + * \param contact The QContact to be removed. + * \param qtError Qt error code. + * \return Error status. + */ +bool CntSymbianEngine::removeContact(const QContactLocalId &id, QContactChangeSet& changeSet, QContactManager::Error& qtError) +{ + // removeContactL() can't throw c++ exception + TRAPD(err, removeContactL(id)); + if(err == KErrNone) + { + //TODO: check what to do with groupsChanged? + changeSet.removedContacts().insert(id); + m_dataBase->appendContactEmitted(id); + } + CntSymbianTransformError::transformError(err, qtError); + return (err==KErrNone); +} + +/*! + * Private leaving implementation for removeContact + */ +int CntSymbianEngine::removeContactL(QContactLocalId id) +{ + // A contact with a zero id is not expected to exist. + // Symbian contact database uses id 0 internally as the id of the + // system template. + if(id == 0) + User::Leave(KErrNotFound); + + //TODO: in future QContactLocalId will be a class so this will need to be changed. + TContactItemId cId = static_cast(id); + + //TODO: add code to remove all relationships. + + m_dataBase->contactDatabase()->DeleteContactL(cId); +#ifdef SYMBIAN_BACKEND_S60_VERSION_32 + // In S60 3.2 hardware (observerd with N96) there is a problem when saving and + // deleting contacts in quick successive manner. At some point the database + // starts leaving with KErrNotReady (-18). This happens randomly at either + // DeleteContactL() or AddNewContactL(). The only only thing that seems to + // help is to compress the database after deleting a contact. + // + // Needles to say that this will have a major negative effect on performance! + // TODO: A better solution must be found. + m_dataBase->contactDatabase()->CompactL(); +#endif + + return 0; +} + +bool CntSymbianEngine::removeContact(const QContactLocalId& contactId, QContactManager::Error& error) +{ + QContactManager::Error err; + QContactLocalId selfCntId = selfContactId(err); // err ignored + QContactChangeSet changeSet; + TBool ret = removeContact(contactId, changeSet, error); + if (ret && contactId == selfCntId ) { + QOwnCardPair ownCard(selfCntId, QContactLocalId(0)); + changeSet.oldAndNewSelfContactId() = ownCard; + } + changeSet.emitSignals(this); + return ret; +} + +void CntSymbianEngine::updateDisplayLabel(QContact& contact) const +{ + QContactManager::Error error(QContactManager::NoError); + QString label = synthesizedDisplayLabel(contact, error); + if(error == QContactManager::NoError) { + contact = setContactDisplayLabel(label, contact); + } +} + +bool CntSymbianEngine::removeContacts(QList *contactIds, QMap *errorMap, QContactManager::Error& error) +{ + error = QContactManager::NoError; + + if (errorMap) { + // if the errormap argument is null, we just don't do fine-grained reporting. + errorMap->clear(); + } + + if (!contactIds) { + error = QContactManager::BadArgumentError; + return false; + } + + QContactManager::Error err; + QContactLocalId selfCntId = selfContactId(err); // err ignored + + QContactChangeSet changeSet; + for (int i = 0; i < contactIds->count(); i++) { + QContactLocalId current = contactIds->at(i); + QContactManager::Error functionError = QContactManager::NoError; + if (!removeContact(current, changeSet, functionError)) { + error = functionError; + if (errorMap) { + errorMap->insert(i, functionError); + } + } else { + (*contactIds)[i] = 0; + if (current == selfCntId ) { + QOwnCardPair ownCard(selfCntId, QContactLocalId(0)); + changeSet.oldAndNewSelfContactId() = ownCard; + } + } + } + changeSet.emitSignals(this); + return (error == QContactManager::NoError); +} + +/* relationships */ + +QStringList CntSymbianEngine::supportedRelationshipTypes(const QString& contactType) const +{ + return m_relationship->supportedRelationshipTypes(contactType); +} + +QList CntSymbianEngine::relationships(const QString& relationshipType, const QContactId& participantId, QContactRelationshipFilter::Role role, QContactManager::Error& error) const +{ + //retrieve the relationships + return m_relationship->relationships(relationshipType, participantId, role, error); +} + +bool CntSymbianEngine::saveRelationship(QContactRelationship* relationship, QContactManager::Error& error) +{ + //affected contacts + QContactChangeSet changeSet; + + //save the relationship + bool returnValue = m_relationship->saveRelationship(&changeSet.addedRelationshipsContacts(), relationship, error); + + //add contacts to the list that shouldn't be emitted + m_dataBase->appendContactsEmitted(changeSet.addedRelationshipsContacts().toList()); + + //emit signals + changeSet.emitSignals(this); + + return returnValue; +} + +QList CntSymbianEngine::saveRelationships(QList* relationships, QContactManager::Error& error) +{ + //affected contacts + QContactChangeSet changeSet; + + //save the relationships + QList returnValue = m_relationship->saveRelationships(&changeSet.addedRelationshipsContacts(), relationships, error); + + //add contacts to the list that shouldn't be emitted + m_dataBase->appendContactsEmitted(changeSet.addedRelationshipsContacts().toList()); + + //emit signals + changeSet.emitSignals(this); + + return returnValue; +} + +bool CntSymbianEngine::removeRelationship(const QContactRelationship& relationship, QContactManager::Error& error) +{ + //affected contacts + QContactChangeSet changeSet; + + //remove the relationship + bool returnValue = m_relationship->removeRelationship(&changeSet.removedRelationshipsContacts(), relationship, error); + + //add contacts to the list that shouldn't be emitted + m_dataBase->appendContactsEmitted(changeSet.removedRelationshipsContacts().toList()); + + //emit signals + changeSet.emitSignals(this); + + return returnValue; +} + +QList CntSymbianEngine::removeRelationships(const QList& relationships, QContactManager::Error& error) +{ + //affected contacts + QContactChangeSet changeSet; + + //remove the relationships + QList returnValue = m_relationship->removeRelationships(&changeSet.removedRelationshipsContacts(), relationships, error); + + //add contacts to the list that shouldn't be emitted + m_dataBase->appendContactsEmitted(changeSet.removedRelationshipsContacts().toList()); + + //emit signals + changeSet.emitSignals(this); + + return returnValue; +} + +QMap CntSymbianEngine::detailDefinitions(const QString& contactType, QContactManager::Error& error) const +{ + // TODO: update for SIM contacts later + if (contactType != QContactType::TypeContact && contactType != QContactType::TypeGroup) { + error = QContactManager::InvalidContactTypeError; + return QMap(); + } + + error = QContactManager::NoError; + + // First get the default definitions + QMap > schemaDefinitions = QContactManagerEngine::schemaDefinitions(); + + // And then ask contact transformer to do the modifications required + QMap schemaForType = schemaDefinitions.value(contactType); + m_transformContact->detailDefinitions(schemaForType, contactType, error); + + return schemaForType; +} + +bool CntSymbianEngine::hasFeature(QContactManager::ManagerFeature feature, const QString& contactType) const +{ + bool returnValue(false); + + // TODO: update for SIM contacts later + if (contactType != QContactType::TypeContact && contactType != QContactType::TypeGroup) + return false; + + switch (feature) { + /* + TODO: + How about the others? like: + Groups, + ActionPreferences, + MutableDefinitions, + Relationships, + ArbitraryRelationshipTypes, + RelationshipOrdering, + DetailOrdering, + SelfContact, + Anonymous, + ChangeLogs + */ + case QContactManager::Groups: + case QContactManager::Relationships: + case QContactManager::SelfContact: { + returnValue = true; + break; + } + + default: + returnValue = false; + } + + return returnValue; +} + +bool CntSymbianEngine::isFilterSupported(const QContactFilter& filter) const +{ + return m_contactFilter->filterSupported(filter); +} + +/* Synthesise the display label of a contact */ +QString CntSymbianEngine::synthesizedDisplayLabel(const QContact& contact, QContactManager::Error& error) const +{ + error = QContactManager::NoError; + return m_displayLabel->synthesizedDisplayLabel(contact, error); +} + +bool CntSymbianEngine::setSelfContactId(const QContactLocalId& contactId, QContactManager::Error& error) +{ + if (contactId <= 0) { + error = QContactManager::BadArgumentError; + return false; + } + + TContactItemId id(contactId); + CContactItem* symContact = 0; + TRAPD(err, + symContact = m_dataBase->contactDatabase()->ReadContactL(id); + m_dataBase->contactDatabase()->SetOwnCardL(*symContact); + ); + delete symContact; + CntSymbianTransformError::transformError(err, error); + return (err==KErrNone); +} + +QContactLocalId CntSymbianEngine::selfContactId(QContactManager::Error& error) const +{ + error = QContactManager::NoError; + QContactLocalId id = 0; + + TContactItemId myCard = m_dataBase->contactDatabase()->OwnCardId(); + if (myCard < 0) { + error = QContactManager::DoesNotExistError; + } + else { + id = myCard; + } + return id; +} + +/*! + * Returns the list of data types supported by the Symbian S60 engine + */ +QList CntSymbianEngine::supportedDataTypes() const +{ + QList st; + st.append(QVariant::String); + + return st; +} + +QString CntSymbianEngine::managerName() const +{ + return CNT_SYMBIAN_MANAGER_NAME; +} + +/* + * 'async' code borrowed from memory engine - actually does sync operations. + * This will be replaced by the thread request worker when it is stable. + */ + + + +/*! \reimp */ +void CntSymbianEngine::requestDestroyed(QContactAbstractRequest* req) +{ + m_asynchronousOperations.removeOne(req); +} + +/*! \reimp */ +bool CntSymbianEngine::startRequest(QContactAbstractRequest* req) +{ + if (!m_asynchronousOperations.contains(req)) + m_asynchronousOperations.enqueue(req); + updateRequestState(req, QContactAbstractRequest::ActiveState); + QTimer::singleShot(0, this, SLOT(performAsynchronousOperation())); + return true; +} + +/*! \reimp */ +bool CntSymbianEngine::cancelRequest(QContactAbstractRequest* req) +{ + updateRequestState(req, QContactAbstractRequest::CanceledState); + return true; +} + +/*! \reimp */ +bool CntSymbianEngine::waitForRequestProgress(QContactAbstractRequest* req, int msecs) +{ + Q_UNUSED(msecs); + + if (!m_asynchronousOperations.removeOne(req)) + return false; // didn't exist. + + // replace at head of queue + m_asynchronousOperations.insert(0, req); + + // and perform the operation. + performAsynchronousOperation(); + + return true; +} + +/*! \reimp */ +bool CntSymbianEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs) +{ + // in our implementation, we always complete any operation we start. + // so, waitForRequestFinished is equivalent to waitForRequestProgress. + return waitForRequestProgress(req, msecs); +} + +/*! + * This slot is called some time after an asynchronous request is started. + * It performs the required operation, sets the result and returns. + */ +void CntSymbianEngine::performAsynchronousOperation() +{ + QContactAbstractRequest *currentRequest; + + // take the first pending request and finish it + if (m_asynchronousOperations.isEmpty()) + return; + currentRequest = m_asynchronousOperations.dequeue(); + + // check to see if it is cancelling; if so, remove it from the queue and return. + if (currentRequest->state() == QContactAbstractRequest::CanceledState) { + return; + } + + // store up changes, and emit signals once at the end of the (possibly batch) operation. + QContactChangeSet changeSet; + + // Now perform the active request and emit required signals. + Q_ASSERT(currentRequest->state() == QContactAbstractRequest::ActiveState); + switch (currentRequest->type()) { + case QContactAbstractRequest::ContactFetchRequest: + { + QContactFetchRequest* r = static_cast(currentRequest); + QContactFilter filter = r->filter(); + QList sorting = r->sorting(); + QStringList defs = r->definitionRestrictions(); + + QContactManager::Error operationError; + QList requestedContacts = QContactManagerEngine::contacts(filter, sorting, defs, operationError); + + // update the request with the results. + if (!requestedContacts.isEmpty() || operationError != QContactManager::NoError) + updateContactFetchRequest(r, requestedContacts, operationError); // emit resultsAvailable() + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + + case QContactAbstractRequest::ContactLocalIdFetchRequest: + { + QContactLocalIdFetchRequest* r = static_cast(currentRequest); + QContactFilter filter = r->filter(); + QList sorting = r->sorting(); + + QContactManager::Error operationError = QContactManager::NoError; + QList requestedContactIds = QContactManagerEngine::contactIds(filter, sorting, operationError); + + if (!requestedContactIds.isEmpty() || operationError != QContactManager::NoError) + updateContactLocalIdFetchRequest(r, requestedContactIds, operationError); // emit resultsAvailable() + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + + case QContactAbstractRequest::ContactSaveRequest: + { + QContactSaveRequest* r = static_cast(currentRequest); + QList contacts = r->contacts(); + + QContactManager::Error operationError = QContactManager::NoError; + QMap errorMap; + saveContacts(&contacts, &errorMap, operationError); + + updateContactSaveRequest(r, contacts, operationError, errorMap); // there will always be results of some form. emit resultsAvailable(). + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + + case QContactAbstractRequest::ContactRemoveRequest: + { + // this implementation provides scant information to the user + // the operation either succeeds (all contacts matching the filter were removed) + // or it fails (one or more contacts matching the filter could not be removed) + // if a failure occurred, the request error will be set to the most recent + // error that occurred during the remove operation. + QContactRemoveRequest* r = static_cast(currentRequest); + QContactManager::Error operationError = QContactManager::NoError; + QList contactsToRemove = r->contactIds(); + QMap errorMap; + + for (int i = 0; i < contactsToRemove.size(); i++) { + QContactManager::Error tempError; + removeContact(contactsToRemove.at(i), changeSet, tempError); + + if (tempError != QContactManager::NoError) { + errorMap.insert(i, tempError); + operationError = tempError; + } + } + + if (!errorMap.isEmpty() || operationError != QContactManager::NoError) + updateContactRemoveRequest(r, operationError, errorMap); // emit resultsAvailable() + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + + case QContactAbstractRequest::DetailDefinitionFetchRequest: + { + QContactDetailDefinitionFetchRequest* r = static_cast(currentRequest); + QContactManager::Error operationError = QContactManager::NoError; + QMap errorMap; + QMap requestedDefinitions; + QStringList names = r->definitionNames(); + if (names.isEmpty()) + names = detailDefinitions(r->contactType(), operationError).keys(); // all definitions. + + QContactManager::Error tempError; + for (int i = 0; i < names.size(); i++) { + QContactDetailDefinition current = detailDefinition(names.at(i), r->contactType(), tempError); + requestedDefinitions.insert(names.at(i), current); + + if (tempError != QContactManager::NoError) { + errorMap.insert(i, tempError); + operationError = tempError; + } + } + + if (!errorMap.isEmpty() || !requestedDefinitions.isEmpty() || operationError != QContactManager::NoError) + updateDefinitionFetchRequest(r, requestedDefinitions, operationError, errorMap); // emit resultsAvailable() + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + +// symbian engine currently does not support mutable definitions. +// +// case QContactAbstractRequest::DetailDefinitionSaveRequest: +// { +// QContactDetailDefinitionSaveRequest* r = static_cast(currentRequest); +// QContactManager::Error operationError = QContactManager::NoError; +// QMap errorMap; +// QList definitions = r->definitions(); +// QList savedDefinitions; +// +// QContactManager::Error tempError; +// for (int i = 0; i < definitions.size(); i++) { +// QContactDetailDefinition current = definitions.at(i); +// saveDetailDefinition(current, r->contactType(), changeSet, tempError); +// savedDefinitions.append(current); +// +// if (tempError != QContactManager::NoError) { +// errorMap.insert(i, tempError); +// operationError = tempError; +// } +// } +// +// // update the request with the results. +// updateDefinitionSaveRequest(r, savedDefinitions, operationError, errorMap); // there will always be results of some form. emit resultsAvailable(). +// updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); +// } +// break; +// +// case QContactAbstractRequest::DetailDefinitionRemoveRequest: +// { +// QContactDetailDefinitionRemoveRequest* r = static_cast(currentRequest); +// QStringList names = r->definitionNames(); +// +// QContactManager::Error operationError = QContactManager::NoError; +// QMap errorMap; +// +// for (int i = 0; i < names.size(); i++) { +// QContactManager::Error tempError; +// removeDetailDefinition(names.at(i), r->contactType(), changeSet, tempError); +// +// if (tempError != QContactManager::NoError) { +// errorMap.insert(i, tempError); +// operationError = tempError; +// } +// } +// +// // there are no results, so just update the status with the error. +// if (!errorMap.isEmpty() || operationError != QContactManager::NoError) +// updateDefinitionRemoveRequest(r, operationError, errorMap); // emit resultsAvailable() +// updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); +// } +// break; + + case QContactAbstractRequest::RelationshipFetchRequest: + { + QContactRelationshipFetchRequest* r = static_cast(currentRequest); + QContactManager::Error operationError = QContactManager::NoError; + QList operationErrors; + QList allRelationships = relationships(QString(), QContactId(), QContactRelationshipFilter::Either, operationError); + QList requestedRelationships; + + // select the requested relationships. + for (int i = 0; i < allRelationships.size(); i++) { + QContactRelationship currRel = allRelationships.at(i); + if (r->first() != QContactId() && r->first() != currRel.first()) + continue; + if (r->second() != QContactId() && r->second() != currRel.second()) + continue; + if (!r->relationshipType().isEmpty() && r->relationshipType() != currRel.relationshipType()) + continue; + requestedRelationships.append(currRel); + } + + // update the request with the results. + if (!requestedRelationships.isEmpty() || operationError != QContactManager::NoError) + updateRelationshipFetchRequest(r, requestedRelationships, operationError); // emit resultsAvailable() + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + + case QContactAbstractRequest::RelationshipRemoveRequest: + { + QContactRelationshipRemoveRequest* r = static_cast(currentRequest); + QContactManager::Error operationError = QContactManager::NoError; + QList relationshipsToRemove = r->relationships(); + QMap errorMap; + + bool foundMatch = false; + for (int i = 0; i < relationshipsToRemove.size(); i++) { + QContactManager::Error tempError; + removeRelationship(relationshipsToRemove.at(i), tempError); + + if (tempError != QContactManager::NoError) { + errorMap.insert(i, tempError); + operationError = tempError; + } + } + + if (foundMatch == false && operationError == QContactManager::NoError) + operationError = QContactManager::DoesNotExistError; + + if (!errorMap.isEmpty() || operationError != QContactManager::NoError) + updateRelationshipRemoveRequest(r, operationError, errorMap); // emit resultsAvailable() + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + + case QContactAbstractRequest::RelationshipSaveRequest: + { + QContactRelationshipSaveRequest* r = static_cast(currentRequest); + QContactManager::Error operationError = QContactManager::NoError; + QMap errorMap; + QList requestRelationships = r->relationships(); + QList savedRelationships; + + QContactManager::Error tempError; + for (int i = 0; i < requestRelationships.size(); i++) { + QContactRelationship current = requestRelationships.at(i); + saveRelationship(¤t, tempError); + savedRelationships.append(current); + + if (tempError != QContactManager::NoError) { + errorMap.insert(i, tempError); + operationError = tempError; + } + } + + // update the request with the results. + updateRelationshipSaveRequest(r, savedRelationships, operationError, errorMap); // there will always be results of some form. emit resultsAvailable(). + updateRequestState(currentRequest, QContactAbstractRequest::FinishedState); + } + break; + + default: // unknown request type. + break; + } + + // now emit any signals we have to emit + changeSet.emitSignals(this); +} + +#ifndef PBK_UNIT_TEST +/* Factory lives here in the basement */ +QContactManagerEngine* CntSymbianFactory::engine(const QMap& parameters, QContactManager::Error& error) +{ + return new CntSymbianEngine(parameters, error); +} + +QString CntSymbianFactory::managerName() const +{ + return CNT_SYMBIAN_MANAGER_NAME; +} + +Q_EXPORT_PLUGIN2(mobapicontactspluginsymbian, CntSymbianFactory); + +#endif //PBK_UNIT_TEST