Add src/engine/engine.pri
[qtcontacts-tracker:hasselmms-qtcontacts-tracker.git] / src / engine / engine.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Mobility Components.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** No Commercial Usage
11 ** This file contains pre-release code and may not be distributed.
12 ** You may use this file in accordance with the terms and conditions
13 ** contained in the Technology Preview License Agreement accompanying
14 ** this package.
15 **
16 ** GNU Lesser General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU Lesser
18 ** General Public License version 2.1 as published by the Free Software
19 ** Foundation and appearing in the file LICENSE.LGPL included in the
20 ** packaging of this file.  Please review the following information to
21 ** ensure the GNU Lesser General Public License version 2.1 requirements
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23 **
24 ** In addition, as a special exception, Nokia gives you certain additional
25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "engine.h"
43 #include "engine_p.h"
44
45 #include <dao/contactdetailschema.h>
46 #include <dao/trackerchangelistener.h>
47
48 #include <requests/contactidfetchrequest.h>
49 #include <requests/contactremoverequest.h>
50 #include <requests/contactsaverequest.h>
51 #include <requests/relationshipfetchrequest.h>
52 #include <requests/relationshipsaverequest.h>
53
54 #include <qcontactdetails.h>
55
56 QTM_USE_NAMESPACE;
57
58 ////////////////////////////////////////////////////////////////////////////////////////////////////
59 // Instances which manage contact engine instances
60 ////////////////////////////////////////////////////////////////////////////////////////////////////
61
62 QContactTrackerEngine::QContactTrackerEngine(const QString& engineName, int engineVersion,
63                                              const QMap<QString, QString>& parameters)
64     : d(new QContactTrackerEngineData)
65 {
66     Q_UNUSED(parameters);
67     d->m_engineName = engineName;
68     d->m_engineVersion = engineVersion;
69     connectToSignals();
70 }
71
72 QContactTrackerEngine::QContactTrackerEngine(const QMap<QString, QString>& parameters)
73     : d(new QContactTrackerEngineData)
74 {
75     Q_UNUSED(parameters);
76     connectToSignals();
77 }
78
79 QContactTrackerEngine::QContactTrackerEngine(const QContactTrackerEngine& other)
80     : QContactManagerEngine(), d(other.d)
81 {
82     connectToSignals();
83 }
84
85 void QContactTrackerEngine::connectToSignals()
86 {
87     TrackerChangeListener *listener = new TrackerChangeListener(this, this);
88
89     connect(listener, SIGNAL(contactsAdded(const QList<QContactLocalId>&)),
90             this, SIGNAL(contactsAdded(const QList<QContactLocalId>&)));
91     connect(listener, SIGNAL(contactsChanged(const QList<QContactLocalId>&)),
92             this, SIGNAL(contactsChanged(const QList<QContactLocalId>&)));
93     connect(listener, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)),
94             this, SIGNAL(contactsRemoved(const QList<QContactLocalId>&)));
95 }
96
97 QContactTrackerEngine::~QContactTrackerEngine()
98 {
99 }
100
101 QContactTrackerEngine&
102 QContactTrackerEngine::operator=(const QContactTrackerEngine& other)
103 {
104     d = other.d;
105     return *this;
106 }
107
108 QContactManagerEngine* QContactTrackerEngine::clone()
109 {
110     // this engine allows sharing - so we increase the reference count.
111     d->m_refCount.ref();
112     return this;
113 }
114
115 void QContactTrackerEngine::deref()
116 {
117     if (not d->m_refCount.deref()) {
118         delete this;
119     }
120 }
121
122 ////////////////////////////////////////////////////////////////////////////////////////////////////
123 // Methods which describe the contact engine's capabilities
124 ////////////////////////////////////////////////////////////////////////////////////////////////////
125
126 QString
127 QContactTrackerEngine::managerName() const
128 {
129     return d->m_engineName;
130 }
131
132 int
133 QContactTrackerEngine::managerVersion() const
134 {
135     return d->m_engineVersion;
136 }
137
138 bool
139 QContactTrackerEngine::hasFeature(QContactManager::ManagerFeature feature,
140                                   const QString& contactType) const
141 {
142     if (!supportedContactTypes().contains(contactType)) {
143         return false;
144     }
145
146     switch (feature) {
147     case QContactManager::ArbitraryRelationshipTypes:
148     case QContactManager::ActionPreferences:
149     case QContactManager::ChangeLogs:
150     case QContactManager::Groups:
151     case QContactManager::Relationships:
152     case QContactManager::SelfContact:
153         return true;
154
155     default:
156         return false;
157     }
158 }
159
160 bool
161 QContactTrackerEngine::isFilterSupported(const QContactFilter& filter) const
162 {
163     switch (filter.type()) {
164     case QContactFilter::InvalidFilter:
165     case QContactFilter::DefaultFilter:
166     case QContactFilter::LocalIdFilter:
167     case QContactFilter::ContactDetailFilter:
168     case QContactFilter::ContactDetailRangeFilter:
169     case QContactFilter::ActionFilter:
170     case QContactFilter::ChangeLogFilter:
171     case QContactFilter::RelationshipFilter:
172         return true;
173
174
175     case QContactFilter::IntersectionFilter: // not yet done
176     case QContactFilter::UnionFilter: // not yet done
177         break;
178     }
179
180     return false;
181 }
182
183 QList<QVariant::Type>
184 QContactTrackerEngine::supportedDataTypes() const
185 {
186     return QTrackerContactDetailSchema::instance().supportedDataTypes().toList();
187 }
188
189 QMap<QString, QContactDetailDefinition>
190 QContactTrackerEngine::detailDefinitions(const QString& contactType,
191                                          QContactManager::Error* error) const
192 {
193     if (contactType != QContactType::TypeContact) {
194         *error = QContactManager::InvalidContactTypeError;
195         return QMap<QString, QContactDetailDefinition>();
196     }
197
198     return QTrackerContactDetailSchema::instance().detailDefinitions();
199 }
200
201 QContactLocalId
202 QContactTrackerEngine::selfContactId(QContactManager::Error* error) const
203 {
204     *error = QContactManager::NoError;
205     return QContactLocalId(0xFFFFFFFF);
206 }
207
208 ////////////////////////////////////////////////////////////////////////////////////////////////////
209 // Synchronous data access methods
210 ////////////////////////////////////////////////////////////////////////////////////////////////////
211
212 static void
213 propagateError(const QContactAbstractRequest &request, QContactManager::Error* error)
214 {
215     if (error) {
216         if (request.isFinished()) {
217             *error = request.error();
218         } else {
219             *error = QContactManager::UnspecifiedError;
220         }
221     }
222 }
223
224 QList<QContactLocalId>
225 QContactTrackerEngine::contactIds(const QList<QContactSortOrder>& sortOrders,
226                                   QContactManager::Error* error) const
227 {
228     return contactIds(QContactFilter(), sortOrders, error);
229 }
230
231 QList<QContactLocalId>
232 QContactTrackerEngine::contactIds(const QContactFilter& filter,
233                                   const QList<QContactSortOrder>& sortOrders,
234                                   QContactManager::Error* error) const
235 {
236     QContactLocalIdFetchRequest request;
237
238     request.setFilter(filter);
239     request.setSorting(sortOrders);
240
241     QContactTrackerEngine engine(*this);
242
243     engine.startRequest(&request);
244     engine.waitForRequestFinished(&request, 10000);  // 10 seconds should be enough
245     propagateError(request, error);
246
247     return request.ids();
248 }
249
250 QList<QContact>
251 QContactTrackerEngine::contacts(const QContactFilter& filter,
252                                 const QList<QContactSortOrder>& sortOrders,
253                                 const QContactFetchHint& fetchHint,
254                                 QContactManager::Error* error) const
255 {
256     QContactFetchRequest request;
257
258     request.setFetchHint(fetchHint);
259     request.setFilter(filter);
260     request.setSorting(sortOrders);
261
262     QContactTrackerEngine engine(*this);
263
264     engine.startRequest(&request);
265     engine.waitForRequestFinished(&request, 10000); // 10 seconds should be enough
266     propagateError(request, error);
267
268     return request.contacts();
269 }
270
271 QContact
272 QContactTrackerEngine::contact(const QContactLocalId& contactId,
273                                const QContactFetchHint& fetchHint,
274                                QContactManager::Error* error) const
275 {
276     // plan to keep this warning for a while - as message to customers using the API
277     qWarning() << Q_FUNC_INFO << "\n"
278             "=============================================================================\n"
279             "API is blocking on dbus roundtrip while accessing tracker. Please, consider\n"
280             "using asynchronous API QContactFetchRequest and not fetching contacts by id -\n"
281             "reading 100 ids and 100 contact by ids is ~100 times slower then reading 100\n"
282             "contacts at once with QContactFetchRequest.\n"
283             "=============================================================================";
284
285     return contactImpl(contactId, fetchHint, error);
286 }
287
288 // Used in tests, removed warning while decided if to provide sync api.
289 // Until then customers are advised to use async.
290 QContact
291 QContactTrackerEngine::contactImpl(const QContactLocalId& contactId,
292                                    const QContactFetchHint& fetchHint,
293                                    QContactManager::Error* error) const
294 {
295     QStringList definitionNames = fetchHint.detailDefinitionsHint();
296
297     if (fetchHint.detailDefinitionsHint().isEmpty()) {
298         definitionNames
299                 << QContactAvatar::DefinitionName
300                 << QContactBirthday::DefinitionName
301                 << QContactAddress::DefinitionName
302                 << QContactEmailAddress::DefinitionName
303                 << QContactDisplayLabel::DefinitionName
304                 << QContactGender::DefinitionName
305                 << QContactAnniversary::DefinitionName
306                 << QContactName::DefinitionName
307                 << QContactOnlineAccount::DefinitionName
308                 << QContactOrganization::DefinitionName
309                 << QContactPhoneNumber::DefinitionName
310                 << QContactOnlineAccount::DefinitionName
311                 << QContactUrl::DefinitionName;
312     }
313
314     QContactFetchHint modifiedHint;
315
316     modifiedHint.setDetailDefinitionsHint(definitionNames);
317
318     QContactLocalIdFilter idFilter;
319
320     idFilter.setIds(QList<QContactLocalId>() << contactId);
321
322     QContactFetchRequest request;
323
324     request.setFetchHint(modifiedHint);
325     request.setFilter(idFilter);
326
327     QContactTrackerEngine engine(*this);
328
329     engine.startRequest(&request);
330     engine.waitForRequestFinished(&request, 10000); // 10 seconds should be enough
331
332     if (not request.isFinished()) {
333         if (error) {
334             *error = QContactManager::UnspecifiedError;
335         }
336
337         return QContact();
338     }
339
340     if(request.contacts().isEmpty()) {
341         if (error) {
342             *error = QContactManager::DoesNotExistError;
343         }
344
345         return QContact();
346     }
347
348     if (error) {
349         *error = request.error();
350     }
351
352     return request.contacts()[0];
353 }
354
355 bool
356 QContactTrackerEngine::saveContact(QContact* contact, QContactManager::Error* error)
357 {
358     if (0 == contact) {
359         if (error) {
360             *error = QContactManager::BadArgumentError;
361         }
362
363         return false;
364     }
365
366     QList<QContact> contactList(QList<QContact>() << *contact);
367     return saveContacts(&contactList, 0, error);
368 }
369
370 bool
371 QContactTrackerEngine::saveContacts(QList<QContact>* contacts,
372                                     QMap<int, QContactManager::Error>* errorMap,
373                                     QContactManager::Error* error)
374 {
375     if (0 == contacts) {
376         if (error) {
377             *error = QContactManager::BadArgumentError;
378         }
379
380         return false;
381     }
382
383     QContactSaveRequest request;
384
385     request.setContacts(*contacts);
386
387     QContactTrackerEngine engine(*this);
388
389     engine.startRequest(&request);
390     engine.waitForRequestFinished(&request, 10000); // 10 seconds should be enough
391
392     if (errorMap) {
393         *errorMap = request.errorMap();
394     }
395
396     if (error) {
397         *error = request.error();
398     }
399
400     for (int i = 0; i < contacts->size(); ++i) {
401         contacts->replace(i, request.contacts().at(i));
402     }
403
404     return request.isFinished() && QContactManager::NoError == request.error();
405 }
406
407 bool
408 QContactTrackerEngine::removeContact(const QContactLocalId& contactId,
409                                      QContactManager::Error* error)
410 {
411     *error = QContactManager::NoError;
412
413     // TODO: Do with LiveNodes when they support strict querying.
414     RDFVariable RDFContact = RDFVariable::fromType<nco::PersonContact>();
415     RDFContact.property<nco::contactUID>() = LiteralValue(QString::number(contactId));
416     RDFSelect query;
417
418     query.addColumn("contact_uri", RDFContact);
419     LiveNodes ncoContacts = ::tracker()->modelQuery(query);
420     if(ncoContacts->rowCount() == 0) {
421         *error = QContactManager::DoesNotExistError;
422         return false;
423     }
424
425
426     Live< nco::PersonContact> ncoContact = ncoContacts->liveNode(0);
427     LiveNodes contactMediums = ncoContact->getHasContactMediums();
428     foreach(Live<nco::ContactMedium> media, contactMediums) {
429         media->remove();
430     }
431     ncoContact->remove();
432
433     return true;
434 }
435
436 bool
437 QContactTrackerEngine::removeContacts(const QList<QContactLocalId>& contactIds,
438                                       QMap<int, QContactManager::Error>* errorMap,
439                                       QContactManager::Error* error)
440 {
441     // Cannot report errors - giving up.
442     if(!errorMap) {
443         *error = QContactManager::BadArgumentError;
444         return false;
445     }
446
447     // let's clear the error hash so there is nothing old haunting us.
448     errorMap->clear();
449
450     for (int i = 0; i < contactIds.count(); i++) {
451         QContactManager::Error lastError;
452         removeContact(contactIds.at(i), &lastError);
453         if (lastError != QContactManager::NoError) {
454             errorMap->insert(i, lastError);
455         }
456     }
457
458     // Returns true if no errors were encountered - false if there was errors.
459     // emit signals removed as they are fired from QContactManager
460     *error = QContactManager::NoError;
461     return (errorMap->isEmpty());
462 }
463
464 ////////////////////////////////////////////////////////////////////////////////////////////////////
465 // Asynchronous data access methods
466 ////////////////////////////////////////////////////////////////////////////////////////////////////
467
468 bool
469 QContactTrackerEngine::startRequest(QContactAbstractRequest* req)
470 {
471     QObject *request = 0;
472
473     switch (req->type())
474     {
475     case QContactAbstractRequest::ContactLocalIdFetchRequest:
476         request = new QTrackerContactIdFetchRequest(req, this);
477         break;
478     case QContactAbstractRequest::ContactFetchRequest:
479         request = new QTrackerContactFetchRequest(req, this);
480         break;
481     case QContactAbstractRequest::ContactSaveRequest:
482         request = new QTrackerContactSaveRequest(req, this);
483         break;
484     case QContactAbstractRequest::RelationshipFetchRequest:
485         request = new QTrackerRelationshipFetchRequest(req, this);
486         break;
487     case QContactAbstractRequest::RelationshipSaveRequest:
488         request = new QTrackerRelationshipSaveRequest(req, this);
489         break;
490     case QContactAbstractRequest::ContactRemoveRequest:
491         request = new QTrackerContactRemoveRequest(req, this);
492         break;
493     default:
494         return false;
495     }
496
497     d->m_requests[req] = request;
498     return true;
499 }
500
501 /*! \reimp */
502 void
503 QContactTrackerEngine::requestDestroyed(QContactAbstractRequest* req)
504 {
505     if (d->m_requests.contains(req)) {
506         delete d->m_requests.take(req);
507     }
508 }
509
510
511 bool
512 QContactTrackerEngine::waitForRequestFinished(QContactAbstractRequest* req, int msecs)
513 {
514     Q_ASSERT(req);
515     if(!req->isActive())
516     {
517         return req->isFinished(); // might be already finished
518     }
519     QTime t;
520     t.start();
521     while(t.elapsed() < msecs || msecs == 0) // 0 for infinite
522     {
523         usleep(10000);
524         QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents);
525         if(req->isFinished())
526             return true;
527     }
528     qDebug() << Q_FUNC_INFO <<"Status Finished" << req->isFinished();
529     return req->isFinished();
530
531 }
532
533 ////////////////////////////////////////////////////////////////////////////////////////////////////
534 // Synthesized contact details
535 ////////////////////////////////////////////////////////////////////////////////////////////////////
536
537 QString
538 QContactTrackerEngine::synthesizedDisplayLabel(const QContact& contact,
539                                                QContactManager::Error* error) const
540 {
541     QString label = QContactManagerEngine::synthesizedDisplayLabel(contact, error);
542
543     if (label.isEmpty()) {
544         label = contact.detail<QContactNickname>().nickname();
545     }
546
547     if(label.isEmpty()) {
548         label = contact.detail<QContactPresence>().nickname();
549     }
550
551     if (label.isEmpty()) {
552         label = contact.detail<QContactEmailAddress>().emailAddress();
553     }
554
555     if (label.isEmpty()) {
556         label = contact.detail<QContactPresence>().nickname();
557     }
558
559     if (label.isEmpty()) {
560         label = contact.detail<QContactPhoneNumber>().number();
561     }
562
563     if (label.isEmpty()) {
564         const QContactName name(contact.detail<QContactName>());
565         label = name.firstName() + QLatin1Char(' ') + name.lastName();
566     }
567
568     return label;
569 }