--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/plugins/contacts/symbiansim/src/cntsymbiansimengine.cpp Wed Aug 25 15:49:42 2010 +0300
@@ -0,0 +1,611 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 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 "cntsymbiansimengine.h"
+#include "cntsymbiansimtransformerror.h"
+#include "cntsimstore.h"
+#include "cntsimcontactfetchrequest.h"
+#include "cntsimcontactlocalidfetchrequest.h"
+#include "cntsimcontactsaverequest.h"
+#include "cntsimcontactremoverequest.h"
+#include "cntsimdetaildefinitionfetchrequest.h"
+#include <qtcontacts.h>
+
+#include <QEventLoop>
+#include <QTimer>
+#include <QDebug>
+
+#include <centralrepository.h>
+
+// Telephony Configuration API
+// Keys under this category are used in defining telephony configuration.
+const TUid KCRUidTelConfiguration = {0x102828B8};
+// Amount of digits to be used in contact matching.
+// This allows a customer to variate the amount of digits to be matched.
+const TUint32 KTelMatchDigits = 0x00000001;
+// Default match length
+const TInt KDefaultMatchLength(7);
+
+
+CntSymbianSimEngineData::CntSymbianSimEngineData()
+ :m_simStore(0)
+{
+
+}
+
+CntSymbianSimEngineData::~CntSymbianSimEngineData()
+{
+ if (ref == 0) {
+ // Remove all unfinished requests.
+ // If the client has behaved itself then there should be none left.
+ while (m_asyncRequests.size()) {
+ QMap<QContactAbstractRequest *, CntAbstractSimRequest *>::iterator itr = m_asyncRequests.begin();
+ delete itr.value();
+ m_asyncRequests.remove(itr.key());
+ qWarning("Dangling async request!");
+ }
+ }
+}
+
+CntSymbianSimEngine::CntSymbianSimEngine(const QMap<QString, QString>& parameters, QContactManager::Error* error)
+{
+ *error = QContactManager::NoError;
+
+ d = new CntSymbianSimEngineData();
+ d->m_simStore = new CntSimStore(this, parameters.value(KParameterKeySimStoreName), error);
+ if (*error != QContactManager::NoError) {
+ //qDebug() << "Failed to open SIM store" << error;
+ return;
+ }
+
+ // Get phone number match length from cenrep
+ d->m_phoneNumberMatchLen = KDefaultMatchLength;
+ TRAP_IGNORE(getMatchLengthL(d->m_phoneNumberMatchLen)); // ignore error and use default value
+
+ if(d->m_simStore->storeInfo().m_storeName == KParameterValueSimStoreNameSdn) {
+ // In case of SDN store we need to check if any SDN contacts exist to
+ // determine if the store is supported or not
+ if(d->m_simStore->storeInfo().m_usedEntries == 0)
+ *error = QContactManager::NotSupportedError;
+ }
+}
+
+CntSymbianSimEngine::CntSymbianSimEngine(const CntSymbianSimEngine &other)
+ :d(other.d)
+{
+
+}
+
+CntSymbianSimEngine::~CntSymbianSimEngine()
+{
+
+}
+
+QString CntSymbianSimEngine::managerName() const
+{
+ return CNT_SYMBIANSIM_MANAGER_NAME;
+}
+
+/*!
+ * 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 the generic (slow) filtering of QContactManagerEngine.
+ */
+QList<QContactLocalId> CntSymbianSimEngine::contactIds(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, QContactManager::Error* error) const
+{
+ QContactLocalIdFetchRequest req;
+ req.setFilter(filter);
+ req.setSorting(sortOrders);
+ executeRequest(&req, error);
+ return req.ids();
+}
+
+QList<QContact> CntSymbianSimEngine::contacts(const QContactFilter& filter, const QList<QContactSortOrder>& sortOrders, const QContactFetchHint& fetchHint, QContactManager::Error* error) const
+{
+ QContactFetchRequest req;
+ req.setFilter(filter);
+ req.setSorting(sortOrders);
+ req.setFetchHint(fetchHint);
+ executeRequest(&req, error);
+ return req.contacts();
+}
+
+/*!
+ * Reads a contact from the Etel store.
+ *
+ * \param contactId The Id of the contact to be retrieved.
+ * \param definitionRestrictions Definition restrictions.
+ * \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 CntSymbianSimEngine::contact(const QContactLocalId& contactId, const QContactFetchHint& fetchHint, QContactManager::Error* error) const
+{
+ QContactFetchRequest req;
+ QContactLocalIdFilter filter;
+ filter.setIds(QList<QContactLocalId>() << contactId);
+ req.setFilter(filter);
+ req.setFetchHint(fetchHint);
+ executeRequest(&req, error);
+ if (req.contacts().count() == 0)
+ return QContact();
+ return req.contacts().at(0);
+}
+
+QString CntSymbianSimEngine::synthesizedDisplayLabel(const QContact& contact, QContactManager::Error* error) const
+{
+ Q_UNUSED(error);
+
+ QContactName name = contact.detail(QContactName::DefinitionName);
+ if(!name.customLabel().isEmpty()) {
+ return name.customLabel();
+ } else {
+ return QString("");
+ }
+}
+
+/*!
+ * Saves the contacts to the Etel store. Only part of the contact's details
+ * can be saved, and some fields may be trimmed to fit to the SIM card.
+ *
+ * \param contacts Contact to be saved.
+ * \param qtError Qt error code.
+ * \return Error status.
+ */
+bool CntSymbianSimEngine::saveContacts(QList<QContact>* contacts, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error* error)
+{
+ if (!contacts) {
+ *error = QContactManager::BadArgumentError;
+ return false;
+ }
+
+ QContactSaveRequest req;
+ req.setContacts(*contacts);
+ executeRequest(&req, error);
+ if (errorMap)
+ *errorMap = req.errorMap();
+ *contacts = req.contacts();
+ return (*error == QContactManager::NoError );
+}
+
+/*!
+ * Removes the specified contact object from the Etel store.
+ *
+ * \param contactId Id of the contact to be removed.
+ * \param qtError Qt error code.
+ * \return Error status.
+ */
+bool CntSymbianSimEngine::removeContacts(const QList<QContactLocalId>& contactIds, QMap<int, QContactManager::Error>* errorMap, QContactManager::Error* error)
+{
+ QContactRemoveRequest req;
+ req.setContactIds(contactIds);
+ executeRequest(&req, error);
+ if (errorMap)
+ *errorMap = req.errorMap();
+ return (*error == QContactManager::NoError);
+}
+
+/*!
+ * Returns a map of identifier to detail definition which are valid for contacts whose type is the given \a contactType
+ * which are valid for the contacts in this store
+ */
+QMap<QString, QContactDetailDefinition> CntSymbianSimEngine::detailDefinitions(const QString& contactType, QContactManager::Error* error) const
+{
+ if (!supportedContactTypes().contains(contactType)) {
+ // Should never happen
+ *error = QContactManager::NotSupportedError;
+ return QMap<QString, QContactDetailDefinition>();
+ }
+
+ // Get store information
+ SimStoreInfo storeInfo = d->m_simStore->storeInfo();
+
+ // the map we will eventually return
+ QMap<QString, QContactDetailDefinition> retn;
+
+ // local variables for reuse
+ QMap<QString, QContactDetailFieldDefinition> fields;
+ QContactDetailFieldDefinition f;
+ QContactDetailDefinition def;
+ QVariantList subTypes;
+
+ // sync target
+ def.setName(QContactSyncTarget::DefinitionName);
+ fields.clear();
+ f.setDataType(QVariant::String);
+ subTypes.clear();
+ subTypes << QString(QLatin1String(KSimSyncTarget));
+ f.setAllowableValues(subTypes);
+ fields.insert(QContactSyncTarget::FieldSyncTarget, f);
+ def.setFields(fields);
+ def.setUnique(true);
+ retn.insert(def.name(), def);
+
+ // type
+ def.setName(QContactType::DefinitionName);
+ fields.clear();
+ f.setDataType(QVariant::String);
+ subTypes.clear();
+ // groups are not supported
+ subTypes << QString(QLatin1String(QContactType::TypeContact));
+ f.setAllowableValues(subTypes);
+ fields.insert(QContactType::FieldType, f); // note: NO CONTEXT!!
+ def.setFields(fields);
+ def.setUnique(true);
+ retn.insert(def.name(), def);
+
+/* TODO
+ // guid
+ def.setName(QContactGuid::DefinitionName);
+ fields.clear();
+ f.setDataType(QVariant::String);
+ f.setAllowableValues(QVariantList());
+ fields.insert(QContactGuid::FieldGuid, f);
+ f.setDataType(QVariant::StringList);
+ f.setAllowableValues(contexts);
+ fields.insert(QContactDetail::FieldContext, f);
+ def.setFields(fields);
+ def.setUnique(false);
+ def.setAccessConstraint(QContactDetailDefinition::CreateOnly);
+ retn.insert(def.name(), def);
+*/
+
+ // display label
+ def.setName(QContactDisplayLabel::DefinitionName);
+ fields.clear();
+ f.setDataType(QVariant::String);
+ f.setAllowableValues(QVariantList());
+ fields.insert(QContactDisplayLabel::FieldLabel, f);
+ def.setFields(fields);
+ def.setUnique(true);
+ retn.insert(def.name(), def);
+
+ // email support needs to be checked run-time, because it is SIM specific
+ if (storeInfo.m_emailSupported) {
+ def.setName(QContactEmailAddress::DefinitionName);
+ fields.clear();
+ f.setDataType(QVariant::String);
+ f.setAllowableValues(QVariantList());
+ fields.insert(QContactEmailAddress::FieldEmailAddress, f);
+ def.setFields(fields);
+ def.setUnique(true);
+ retn.insert(def.name(), def);
+ }
+
+ // phone number
+ def.setName(QContactPhoneNumber::DefinitionName);
+ fields.clear();
+ f.setDataType(QVariant::String);
+ f.setAllowableValues(QVariantList());
+ fields.insert(QContactPhoneNumber::FieldNumber, f);
+ // TODO: subtypes supported in case a sim contact can have multiple phone numbers?
+ def.setFields(fields);
+ if (storeInfo.m_additionalNumberSupported) {
+ // multiple numbers supported
+ def.setUnique(false);
+ } else {
+ // only one phone number allowed
+ def.setUnique(true);
+ }
+ retn.insert(def.name(), def);
+
+ // nickname support needs to be checked run-time, because it is SIM specific
+ if (storeInfo.m_secondNameSupported) {
+ def.setName(QContactNickname::DefinitionName);
+ fields.clear();
+ f.setDataType(QVariant::String);
+ f.setAllowableValues(QVariantList());
+ fields.insert(QContactNickname::FieldNickname, f);
+ def.setFields(fields);
+ def.setUnique(true);
+ retn.insert(def.name(), def);
+ }
+
+ // name
+ def.setName(QContactName::DefinitionName);
+ fields.clear();
+ f.setDataType(QVariant::String);
+ f.setAllowableValues(QVariantList());
+ fields.insert(QContactName::FieldCustomLabel, f);
+ def.setFields(fields);
+ def.setUnique(true);
+ retn.insert(def.name(), def);
+
+ return retn;
+}
+
+void CntSymbianSimEngine::requestDestroyed(QContactAbstractRequest* req)
+{
+ if (d->m_asyncRequests.contains(req)) {
+ delete d->m_asyncRequests.take(req);
+ }
+}
+
+bool CntSymbianSimEngine::startRequest(QContactAbstractRequest* req)
+{
+ // Don't allow two async requests to be active at the same time.
+ // The RMobilePhoneBookStore cannot handle it.
+ foreach (QContactAbstractRequest* r, d->m_asyncRequests.keys()) {
+ if (r->isActive()) {
+ // TODO: Should we set the error for the request also?
+ return false;
+ }
+ }
+
+ // Check for existing request and start again
+ if (d->m_asyncRequests.contains(req)) {
+ return d->m_asyncRequests.value(req)->start();
+ }
+
+ // Existing request not found. Create a new one.
+ CntAbstractSimRequest* simReq = 0;
+ switch (req->type())
+ {
+ case QContactAbstractRequest::ContactFetchRequest:
+ {
+ QContactFetchRequest* r = static_cast<QContactFetchRequest*>(req);
+ simReq = new CntSimContactFetchRequest(this, r);
+ }
+ break;
+
+ case QContactAbstractRequest::ContactLocalIdFetchRequest:
+ {
+ QContactLocalIdFetchRequest* r = static_cast<QContactLocalIdFetchRequest*>(req);
+ simReq = new CntSimContactLocalIdFetchRequest(this, r);
+ }
+ break;
+
+ case QContactAbstractRequest::ContactSaveRequest:
+ {
+ QContactSaveRequest* r = static_cast<QContactSaveRequest*>(req);
+ simReq = new CntSimContactSaveRequest(this, r);
+ }
+ break;
+
+ case QContactAbstractRequest::ContactRemoveRequest:
+ {
+ QContactRemoveRequest* r = static_cast<QContactRemoveRequest*>(req);
+ simReq = new CntSimContactRemoveRequest(this, r);
+ }
+ break;
+
+ case QContactAbstractRequest::DetailDefinitionFetchRequest:
+ {
+ QContactDetailDefinitionFetchRequest* r = static_cast<QContactDetailDefinitionFetchRequest*>(req);
+ simReq = new CntSimDetailDefinitionFetchRequest(this, r);
+ }
+ break;
+
+ case QContactAbstractRequest::DetailDefinitionSaveRequest:
+ case QContactAbstractRequest::DetailDefinitionRemoveRequest:
+ case QContactAbstractRequest::RelationshipFetchRequest:
+ case QContactAbstractRequest::RelationshipSaveRequest:
+ case QContactAbstractRequest::RelationshipRemoveRequest:
+ // fall through.
+ default: // unknown request type.
+ break;
+ }
+
+ if (simReq) {
+ d->m_asyncRequests.insert(req, simReq);
+ return simReq->start();
+ }
+
+ return false;
+}
+
+bool CntSymbianSimEngine::cancelRequest(QContactAbstractRequest* req)
+{
+ if (d->m_asyncRequests.contains(req))
+ return d->m_asyncRequests.value(req)->cancel();
+ return false;
+}
+
+bool CntSymbianSimEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs)
+{
+ if (!d->m_asyncRequests.contains(req))
+ return false;
+
+ if (req->state() != QContactAbstractRequest::ActiveState)
+ return false;
+
+ QEventLoop *loop = new QEventLoop(this);
+ QObject::connect(req, SIGNAL(resultsAvailable()), loop, SLOT(quit()));
+
+ // NOTE: zero means wait forever
+ if (msecs > 0)
+ QTimer::singleShot(msecs, loop, SLOT(quit()));
+
+ loop->exec();
+ loop->disconnect();
+ loop->deleteLater();
+
+ return (req->state() == QContactAbstractRequest::FinishedState);
+}
+
+/*!
+ * Returns true if the given feature \a feature is supported by the manager,
+ * for the specified type of contact \a contactType
+ */
+bool CntSymbianSimEngine::hasFeature(QContactManager::ManagerFeature feature, const QString& contactType) const
+{
+ Q_UNUSED(feature);
+ Q_UNUSED(contactType);
+ // We don't support anything in the ManagerFeature
+ return false;
+}
+
+/*!
+ Returns a whether the supplied \a filter can be implemented
+ natively by this engine. If not, the base class implementation
+ will emulate the functionality.
+ */
+bool CntSymbianSimEngine::isFilterSupported(const QContactFilter& filter) const
+{
+ if (filter.type() == QContactFilter::ContactDetailFilter) {
+ QContactDetailFilter f(filter);
+ if (f.detailDefinitionName() == QContactPhoneNumber::DefinitionName &&
+ f.detailFieldName() == QContactPhoneNumber::FieldNumber &&
+ f.matchFlags() == QContactFilter::MatchPhoneNumber)
+ return true;
+ }
+ return false;
+}
+
+/*!
+ * Returns the list of data types supported by the manager
+ */
+QStringList CntSymbianSimEngine::supportedContactTypes() const
+{
+ // TODO: groups supported by some USIM cards?
+ return QStringList() << QContactType::TypeContact;
+}
+
+void CntSymbianSimEngine::updateDisplayLabel(QContact& contact) const
+{
+ QContactManager::Error error(QContactManager::NoError);
+ QString label = synthesizedDisplayLabel(contact, &error);
+ if(error == QContactManager::NoError) {
+ setContactDisplayLabel(&contact, label);
+ }
+}
+
+void CntSymbianSimEngine::setReadOnlyAccessConstraint(QContactDetail* detail) const
+{
+ setDetailAccessConstraints(detail, QContactDetail::ReadOnly);
+}
+
+
+/*!
+ Returns true if the supplied contact \a contact matches the supplied filter \a filter.
+ */
+bool CntSymbianSimEngine::filter(const QContactFilter &filter, const QContact &contact)
+{
+ // Special handling for phonenumber matching:
+ // Matching is done from the right by using a configurable number of digits.
+ // Default number of digits is 7. So for example if we filter with number
+ // +358505555555 the filter should match to +358505555555 and 0505555555.
+ if (filter.type() == QContactFilter::ContactDetailFilter)
+ {
+ QContactDetailFilter f(filter);
+ if (f.detailDefinitionName() == QContactPhoneNumber::DefinitionName &&
+ f.detailFieldName() == QContactPhoneNumber::FieldNumber &&
+ f.matchFlags() == QContactFilter::MatchPhoneNumber)
+ {
+ QString matchNumber = f.value().toString().right(d->m_phoneNumberMatchLen);
+ QList<QContactPhoneNumber> pns = contact.details<QContactPhoneNumber>();
+ foreach (QContactPhoneNumber pn, pns) {
+ QString number = pn.number().right(d->m_phoneNumberMatchLen);
+ if (number == matchNumber)
+ return true;
+ }
+ return false;
+ }
+ }
+ return QContactManagerEngine::testFilter(filter, contact);
+}
+
+/*!
+ * Executes an asynchronous request so that it will appear synchronous. This is
+ * used internally in all synchronous functions. This way we only need to
+ * implement the matching asynchronous request.
+ *
+ * \param req Request to be run.
+ * \param qtError Qt error code.
+ * \return true if succesfull, false if unsuccesfull.
+ */
+bool CntSymbianSimEngine::executeRequest(QContactAbstractRequest *req, QContactManager::Error* qtError) const
+{
+ *qtError = QContactManager::NoError;
+
+ // TODO:
+ // Remove this code when threads-branch is merged to master. Then this code
+ // should not be needed because the default implementation at QContactManager
+ // is using the asynchronous requests in a similar manner to implement
+ // the synchronous functions.
+
+ // Create a copy engine to workaround this functions const qualifier
+ CntSymbianSimEngine engine(*this);
+
+ // Mimic the way how async requests are normally run
+ if (!engine.startRequest(req)) {
+ *qtError = QContactManager::LockedError;
+ } else {
+ if (!engine.waitForRequestFinished(req, 0)) // no timeout
+ *qtError = QContactManager::UnspecifiedError;
+ }
+ engine.requestDestroyed(req);
+
+ if (req->error())
+ *qtError = req->error();
+
+ return (*qtError == QContactManager::NoError);
+}
+
+/*
+ * Get the match length setting used in MatchPhoneNumber type filtering.
+ * \a matchLength Phone number digits to be used in matching (counted from
+ * right).
+ */
+void CntSymbianSimEngine::getMatchLengthL(int &matchLength)
+{
+ //Get number of digits used to match
+ CRepository* repository = CRepository::NewL(KCRUidTelConfiguration);
+ CleanupStack::PushL(repository);
+ User::LeaveIfError(repository->Get(KTelMatchDigits, matchLength));
+ CleanupStack::PopAndDestroy(repository);
+}
+
+QContactManagerEngine* CntSymbianSimFactory::engine(const QMap<QString, QString>& parameters, QContactManager::Error* error)
+{
+ CntSymbianSimEngine *engine = new CntSymbianSimEngine(parameters, error);
+ if(*error != QContactManager::NoError) {
+ delete engine;
+ return 0;
+ }
+ return engine;
+}
+
+QString CntSymbianSimFactory::managerName() const
+{
+ return CNT_SYMBIANSIM_MANAGER_NAME;
+}
+
+Q_EXPORT_PLUGIN2(qtcontacts_symbiansim, CntSymbianSimFactory);