~ci-train-bot/history-service/history-service-ubuntu-zesty-2629

« back to all changes in this revision

Viewing changes to historyprivate/contactmatcher.cpp

Request contact information for all known participants on history-daemon initialization, and use this cached information on the models.
Approved by: Tiago Salem Herrmann, PS Jenkins bot

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
1
/*
2
 
 * Copyright (C) 2014 Canonical, Ltd.
 
2
 * Copyright (C) 2014-2015 Canonical, Ltd.
3
3
 *
4
4
 * Authors:
5
5
 *  Gustavo Pichorim Boiko <gustavo.boiko@canonical.com>
84
84
    return &self;
85
85
}
86
86
 
87
 
QVariantMap ContactMatcher::contactInfo(const QString &accountId, const QString &identifier)
 
87
/**
 
88
 * \brief Returns the contact information for the given \param identifier, taking into account
 
89
 * the addressable fields of the given \param accountId.
 
90
 * If \param synchronous is specified, a blocking synchronous request will be made to the contact
 
91
 * manager to return the specified data.
 
92
 *
 
93
 * Note that synchronous requests should only be placed after \ref TelepathyHelper is ready.
 
94
 */
 
95
QVariantMap ContactMatcher::contactInfo(const QString &accountId, const QString &identifier, bool synchronous)
88
96
{
89
97
    InternalContactMap &internalMap = mContactMap[accountId];
90
98
 
93
101
        return internalMap[identifier];
94
102
    }
95
103
 
96
 
    // now if there was no string match, try phone number matching if the account supports phone numbers
97
 
    if (addressableFields(accountId).contains("tel")) {
98
 
        Q_FOREACH(const QString &key, internalMap.keys()) {
99
 
            if (PhoneUtils::comparePhoneNumbers(key, identifier)) {
100
 
                return internalMap[key];
101
 
            }
102
 
        }
103
 
    }
104
 
 
 
104
    QVariantMap map;
105
105
    // and if there was no match, asynchronously request the info, and return an empty map for now
106
106
    if (TelepathyHelper::instance()->ready()) {
107
 
        requestContactInfo(accountId, identifier);
108
 
    } else {
 
107
        map = requestContactInfo(accountId, identifier, synchronous);
 
108
    } else if (!synchronous) {
109
109
        RequestInfo info{accountId, identifier};
110
110
        mPendingRequests.append(info);
111
111
    }
112
 
    QVariantMap map;
113
112
    map[History::FieldIdentifier] = identifier;
 
113
    map[History::FieldAccountId] = accountId;
114
114
    mContactMap[accountId][identifier] = map;
115
115
    return map;
116
116
}
117
117
 
118
 
QVariantList ContactMatcher::contactInfo(const QString &accountId, const QStringList &identifiers)
 
118
QVariantList ContactMatcher::contactInfo(const QString &accountId, const QStringList &identifiers, bool synchronous)
119
119
{
120
120
    QVariantList contacts;
121
121
    Q_FOREACH(const QString &identifier, identifiers) {
122
 
        contacts << contactInfo(accountId, identifier);
 
122
        contacts << contactInfo(accountId, identifier, synchronous);
123
123
    }
124
124
    return contacts;
125
125
}
126
126
 
 
127
void ContactMatcher::watchIdentifier(const QString &accountId, const QString &identifier, const QVariantMap &currentInfo)
 
128
{
 
129
    // only add the identifier to the map of watched identifiers
 
130
    QVariantMap map = currentInfo;
 
131
    map[History::FieldIdentifier] = identifier;
 
132
    mContactMap[accountId][identifier] = map;
 
133
}
 
134
 
127
135
void ContactMatcher::onContactsAdded(QList<QContactId> ids)
128
136
{
129
137
    QList<QContact> contacts = mManager->contacts(ids);
140
148
        for (; it2 != end2; ++it2) {
141
149
            QString identifier = it2.key();
142
150
            // skip entries that already have a match
143
 
            if (it2.value().contains(History::FieldContactId)) {
 
151
            if (hasMatch(it2.value())) {
144
152
                continue;
145
153
            }
146
154
 
147
155
            // now for each entry not populated, check if it matches one of the newly added contacts
148
156
            Q_FOREACH(const QContact &contact, contacts) {
149
 
                if (matchAndUpdate(accountId, identifier, contact)) {
 
157
                QVariantMap map = matchAndUpdate(accountId, identifier, contact);
 
158
                if (hasMatch(map)){
150
159
                    break;
151
160
                }
152
161
            }
174
183
            QString identifier = it2.key();
175
184
 
176
185
            Q_FOREACH(const QContact &contact, contacts) {
177
 
                bool previousMatch = (contactInfo[History::FieldContactId].toString() == contact.id().toString());
178
 
                if (matchAndUpdate(accountId, identifier, contact)) {
 
186
                bool previousMatch = (contactInfo.contains(History::FieldContactId) &&
 
187
                                      contactInfo[History::FieldContactId].toString() == contact.id().toString());
 
188
                QVariantMap map = matchAndUpdate(accountId, identifier, contact);
 
189
                if (hasMatch(map)){
179
190
                    break;
180
191
                } else if (previousMatch) {
181
192
                    // if there was a previous match but it does not match anymore, try to match the phone number
207
218
        QStringList identifiersToMatch;
208
219
 
209
220
        for (; it2 != end2; ++it2) {
 
221
            QVariantMap &info = it2.value();
210
222
            // skip entries that didn't have a match
211
 
            if (!it2.value().contains(History::FieldContactId)) {
 
223
            if (!hasMatch(info)) {
212
224
                continue;
213
225
            }
214
226
 
215
227
            Q_FOREACH(const QContactId &id, ids) {
216
 
                if (id.toString() == it2.value()[History::FieldContactId].toString()) {
 
228
                if (id.toString() == info[History::FieldContactId].toString()) {
217
229
                    identifiersToMatch << it2.key();
218
230
                    break;
219
231
                }
245
257
            QVariantMap info;
246
258
            info[History::FieldIdentifier] = identifier;
247
259
            Q_EMIT contactInfoChanged(accountId, identifier, info);
 
260
            requestContactInfo(accountId, identifier);
248
261
        }
249
262
    }
250
263
}
277
290
 
278
291
}
279
292
 
280
 
void ContactMatcher::requestContactInfo(const QString &accountId, const QString &identifier)
 
293
/**
 
294
 * \brief Requests contact info, and if the preference is for a synchronous request returns the contact information in a
 
295
 * QVariantMap. For asynchronous requests, an empty QVariantMap is returned.
 
296
 */
 
297
QVariantMap ContactMatcher::requestContactInfo(const QString &accountId, const QString &identifier, bool synchronous)
281
298
{
282
299
    QStringList addressableVCardFields = addressableFields(accountId);
283
300
    if (addressableVCardFields.isEmpty()) {
284
301
        // FIXME: add support for generic accounts
285
 
        return;
 
302
        return QVariantMap();
286
303
    }
287
304
 
288
305
    bool phoneCompare = addressableVCardFields.contains("tel");
289
306
 
290
 
    // check if there is a request already going on for the given contact
291
 
    Q_FOREACH(const RequestInfo &info, mRequests.values()) {
292
 
        if (info.accountId != accountId) {
293
 
            // skip to the next item
294
 
            continue;
295
 
        }
296
 
 
297
 
        if (info.identifier == identifier ||
298
 
            phoneCompare && PhoneUtils::comparePhoneNumbers(info.identifier, identifier)) {
299
 
            // if so, just wait for it to finish
300
 
            return;
301
 
        }
302
 
    }
303
 
 
304
 
    QContactFetchRequest *request = new QContactFetchRequest(this);
305
307
    QContactFetchHint hint;
306
308
    hint.setMaxCountHint(1);
307
309
    // FIXME: maybe we need to fetch the full contact?
309
311
                                                                << QContactDetail::TypePhoneNumber
310
312
                                                                << QContactDetail::TypeAvatar
311
313
                                                                << QContactDetail::TypeExtendedDetail);
312
 
    request->setFetchHint(hint);
313
314
 
314
315
    QContactUnionFilter topLevelFilter;
315
316
    Q_FOREACH(const QString &field, addressableVCardFields) {
336
337
        }
337
338
    }
338
339
 
339
 
    request->setFilter(topLevelFilter);
340
 
    request->setManager(mManager);
341
 
    connect(request,
342
 
            SIGNAL(stateChanged(QContactAbstractRequest::State)),
343
 
            SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
344
 
 
345
 
    RequestInfo info;
346
 
    info.accountId = accountId;
347
 
    info.identifier = identifier;
348
 
    mRequests[request] = info;
349
 
    request->start();
 
340
    if (synchronous) {
 
341
        QList<QContact> contacts = mManager->contacts(topLevelFilter, QList<QContactSortOrder>(), hint);
 
342
        if (contacts.isEmpty()) {
 
343
            return QVariantMap();
 
344
        }
 
345
        // for synchronous requests, return the results right away.
 
346
        return matchAndUpdate(accountId, identifier, contacts.first());
 
347
    } else {
 
348
        // check if there is a request already going on for the given contact
 
349
        Q_FOREACH(const RequestInfo &info, mRequests.values()) {
 
350
            if (info.accountId != accountId) {
 
351
                // skip to the next item
 
352
                continue;
 
353
            }
 
354
 
 
355
            if (info.identifier == identifier) {
 
356
                // if so, just wait for it to finish
 
357
                return QVariantMap();
 
358
            }
 
359
        }
 
360
 
 
361
        QContactFetchRequest *request = new QContactFetchRequest(this);
 
362
        request->setFetchHint(hint);
 
363
        request->setFilter(topLevelFilter);
 
364
        request->setManager(mManager);
 
365
        connect(request,
 
366
                SIGNAL(stateChanged(QContactAbstractRequest::State)),
 
367
                SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
 
368
 
 
369
        RequestInfo info;
 
370
        info.accountId = accountId;
 
371
        info.identifier = identifier;
 
372
        mRequests[request] = info;
 
373
        request->start();
 
374
    }
 
375
    return QVariantMap();
350
376
}
351
377
 
352
378
QVariantList ContactMatcher::toVariantList(const QList<int> &list)
358
384
    return variantList;
359
385
}
360
386
 
361
 
bool ContactMatcher::matchAndUpdate(const QString &accountId, const QString &identifier, const QContact &contact)
 
387
/**
 
388
 * \brief Matches contact data against the given identifier. If the match succeeds, return the updated data in a
 
389
 * QVariantMap, returns an empty map otherwise.
 
390
 */
 
391
QVariantMap ContactMatcher::matchAndUpdate(const QString &accountId, const QString &identifier, const QContact &contact)
362
392
{
 
393
    QVariantMap contactInfo;
 
394
    contactInfo[History::FieldIdentifier] = identifier;
 
395
    contactInfo[History::FieldAccountId] = accountId;
 
396
 
363
397
    if (contact.isEmpty()) {
364
 
        return false;
 
398
        return contactInfo;
365
399
    }
366
400
 
367
401
    QStringList fields = addressableFields(accountId);
368
 
    QVariantMap contactInfo;
369
402
    bool match = false;
370
403
 
 
404
    int fieldsCount = fields.count();
371
405
    Q_FOREACH(const QString &field, fields) {
372
406
        if (field == "tel") {
373
 
            Q_FOREACH(const QContactPhoneNumber number, contact.details(QContactDetail::TypePhoneNumber)) {
 
407
            QList<QContactDetail> details = contact.details(QContactDetail::TypePhoneNumber);
 
408
            Q_FOREACH(const QContactPhoneNumber number, details) {
374
409
                if (PhoneUtils::comparePhoneNumbers(number.number(), identifier)) {
375
410
                    QVariantMap detailProperties;
376
411
                    detailProperties["phoneSubTypes"] = toVariantList(number.subTypes());
397
432
    }
398
433
 
399
434
    if (match) {
400
 
        contactInfo[History::FieldIdentifier] = identifier;
401
435
        contactInfo[History::FieldContactId] = contact.id().toString();
402
436
        contactInfo[History::FieldAlias] = QContactDisplayLabel(contact.detail(QContactDetail::TypeDisplayLabel)).label();
403
437
        contactInfo[History::FieldAvatar] = QContactAvatar(contact.detail(QContactDetail::TypeAvatar)).imageUrl().toString();
407
441
    }
408
442
 
409
443
 
410
 
    return false;
 
444
    return contactInfo;
411
445
}
412
446
 
413
447
QStringList ContactMatcher::addressableFields(const QString &accountId)
425
459
 
426
460
    return fields;
427
461
}
 
462
 
 
463
bool ContactMatcher::hasMatch(const QVariantMap &map) const
 
464
{
 
465
    return (map.contains(History::FieldContactId) && !map[History::FieldContactId].toString().isEmpty());
 
466
}