[Merge] lp:~boiko/history-service/contact_aware_models into lp:history-service

Gustavo Pichorim Boiko gustavo.boiko at canonical.com
Fri Sep 12 20:09:16 UTC 2014


Diff comments replied.

Diff comments:

> === modified file 'CMakeLists.txt'
> --- CMakeLists.txt	2013-11-27 21:04:38 +0000
> +++ CMakeLists.txt	2014-09-09 13:18:46 +0000
> @@ -29,6 +29,7 @@
>  # Instruct CMake to run moc automatically when needed.
>  set(CMAKE_AUTOMOC ON)
>  
> +find_package(Qt5Contacts)
>  find_package(Qt5Core)
>  find_package(Qt5DBus)
>  find_package(Qt5Qml)
> 
> === modified file 'Ubuntu/History/CMakeLists.txt'
> --- Ubuntu/History/CMakeLists.txt	2014-08-12 13:54:32 +0000
> +++ Ubuntu/History/CMakeLists.txt	2014-09-09 13:18:46 +0000
> @@ -1,6 +1,7 @@
>  # QML plugin
>  
>  set(plugin_SRCS
> +    contactmatcher.cpp
>      historyeventmodel.cpp
>      historygroupedeventsmodel.cpp
>      historythreadgroupingproxymodel.cpp
> @@ -15,6 +16,7 @@
>      )
>  
>  set(plugin_HDRS
> +    contactmatcher_p.h
>      historyeventmodel.h
>      historygroupedeventsmodel.h
>      historythreadgroupingproxymodel.h
> @@ -34,7 +36,7 @@
>  
>  add_library(history-qml SHARED ${plugin_SRCS} ${plugin_HDRS})
>  
> -qt5_use_modules(history-qml Core Qml Quick)
> +qt5_use_modules(history-qml Contacts Core Qml Quick)
>  
>  target_link_libraries(history-qml
>      historyservice
> 
> === added file 'Ubuntu/History/contactmatcher.cpp'
> --- Ubuntu/History/contactmatcher.cpp	1970-01-01 00:00:00 +0000
> +++ Ubuntu/History/contactmatcher.cpp	2014-09-09 13:18:46 +0000
> @@ -0,0 +1,264 @@
> +/*
> + * Copyright (C) 2014 Canonical, Ltd.
> + *
> + * Authors:
> + *  Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> + *
> + * This file is part of history-service.
> + *
> + * history-service is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * history-service is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#include "contactmatcher_p.h"
> +#include "phoneutils_p.h"
> +#include "types.h"
> +#include <QContact>
> +#include <QContactAvatar>
> +#include <QContactDisplayLabel>
> +#include <QContactFilter>
> +#include <QContactPhoneNumber>
> +
> +using namespace QtContacts;
> +
> +ContactMatcher::ContactMatcher(QObject *parent) :
> +    QObject(parent), mManager(new QContactManager("galera"))
> +{
> +    connect(mManager,
> +            SIGNAL(contactsAdded(QList<QContactId>)),
> +            SLOT(onContactsAdded(QList<QContactId>)));
> +    connect(mManager,
> +            SIGNAL(contactsChanged(QList<QContactId>)),
> +            SLOT(onContactsChanged(QList<QContactId>)));
> +    connect(mManager,
> +            SIGNAL(contactsRemoved(QList<QContactId>)),
> +            SLOT(onContactsRemoved(QList<QContactId>)));
> +}

Done.

> +
> +ContactMatcher *ContactMatcher::instance()
> +{
> +    static ContactMatcher *self = new ContactMatcher();

Done

> +    return self;
> +}
> +
> +QVariantMap ContactMatcher::contactInfo(const QString &phoneNumber)
> +{
> +    // first do a simple stirng match on the map

Done

> +    if (mContactMap.contains(phoneNumber)) {
> +        return mContactMap[phoneNumber];
> +    }
> +
> +    // now if there was no string match, try phone number matching
> +    Q_FOREACH(const QString &key, mContactMap.keys()) {
> +        if (PhoneUtils::comparePhoneNumbers(key, phoneNumber)) {
> +            return mContactMap[key];
> +        }
> +    }
> +
> +    // and if there was no match, asynchronously request the info, and return an empty map for now
> +    requestContactInfo(phoneNumber);
> +    QVariantMap map;
> +    map[History::FieldPhoneNumber] = phoneNumber;
> +    return map;
> +}
> +
> +QVariantList ContactMatcher::contactInfo(const QStringList &numbers)
> +{
> +    QVariantList contacts;
> +    Q_FOREACH(const QString &number, numbers) {
> +        contacts << contactInfo(number);
> +    }
> +    return contacts;
> +}
> +
> +void ContactMatcher::onContactsAdded(QList<QContactId> ids)
> +{
> +    QList<QContact> contacts = mManager->contacts(ids);
> +
> +    // walk through the list of requested phone numbers
> +    ContactMap::iterator it = mContactMap.begin();
> +    ContactMap::iterator end = mContactMap.end();
> +    for (; it != end; ++it) {
> +        QString phoneNumber = it.key();
> +        // skip entries that already have a match
> +        if (it.value().contains(History::FieldContactId)) {
> +            continue;
> +        }
> +
> +        // now for each entry not populated, check if it matches one of the newly added contacts
> +        bool found = false;
> +        Q_FOREACH(const QContact &contact, contacts) {
> +            Q_FOREACH(const QContactPhoneNumber number, contact.details(QContactDetail::TypePhoneNumber)) {
> +                if (PhoneUtils::comparePhoneNumbers(number.number(), phoneNumber)) {

Because there I was doing a simple lookup on the map, and here I am actually iterating through all the keys to compare against the contacts, so there is no point in doing string comparison here. Also, the phone comparing function already does the string comparing inside as an optimization.

> +                    found = true;
> +                    populateInfo(phoneNumber, contact);
> +                    break;
> +                }

I don't. Look at the lines below, if(found) break;

> +            }
> +
> +            if (found) {
> +                break;
> +            }
> +        }
> +    }
> +}
> +
> +void ContactMatcher::onContactsChanged(QList<QContactId> ids)
> +{
> +    QStringList phoneNumbersToMatch;
> +
> +    QList<QContact> contacts = mManager->contacts(ids);
> +    // walk through the list of requested phone numbers
> +    ContactMap::iterator it = mContactMap.begin();
> +    ContactMap::iterator end = mContactMap.end();
> +    for (; it != end; ++it) {
> +        QVariantMap &contactInfo = it.value();
> +        QString phoneNumber = it.key();
> +
> +        Q_FOREACH(const QContact &contact, contacts) {
> +            bool previousMatch = (contactInfo[History::FieldContactId].toString() == contact.id().toString());
> +            bool found = false;
> +            Q_FOREACH(const QContactPhoneNumber number, contact.details(QContactDetail::TypePhoneNumber)) {
> +                if (PhoneUtils::comparePhoneNumbers(number.number(), phoneNumber)) {
> +                    found = true;
> +                    break;
> +                }
> +            }
> +
> +            if (found) {
> +                populateInfo(phoneNumber, contact);
> +                break;
> +            } else if (previousMatch) {
> +                // if there was a previous match but it does not match anymore, try to match the phone number
> +                // to a different contact
> +                phoneNumbersToMatch << phoneNumber;
> +                break;
> +            }
> +        }
> +    }
> +
> +    Q_FOREACH(const QString &phoneNumber, phoneNumbersToMatch) {
> +        mContactMap.remove(phoneNumber);
> +        requestContactInfo(phoneNumber);
> +    }

This is for the case that two contacts have the phone number, and it is removed from one of them. In that case I need to do a new lookup to get the second contact matching this phone number.

> +}
> +
> +void ContactMatcher::onContactsRemoved(QList<QContactId> ids)
> +{
> +    QStringList phoneNumbersToMatch;
> +
> +    // search for entries that were matching this  contact
> +    ContactMap::iterator it = mContactMap.begin();
> +    ContactMap::iterator end = mContactMap.end();
> +    for (; it != end; ++it) {
> +        // skip entries that didn't have a match
> +        if (!it.value().contains(History::FieldContactId)) {
> +            continue;
> +        }
> +
> +        Q_FOREACH(const QContactId &id, ids) {
> +            if (id.toString() == it.value()[History::FieldContactId].toString()) {
> +                phoneNumbersToMatch << it.key();
> +                break;
> +            }
> +        }
> +    }
> +
> +    // now make sure to try a new match on the phone numbers whose contact was removed
> +    Q_FOREACH(const QString &phoneNumber, phoneNumbersToMatch) {
> +        mContactMap.remove(phoneNumber);
> +        requestContactInfo(phoneNumber);
> +    }
> +}
> +
> +void ContactMatcher::onRequestStateChanged(QContactAbstractRequest::State state)
> +{
> +    QContactFetchRequest *request = qobject_cast<QContactFetchRequest*>(sender());
> +    if (!request) {
> +        return;
> +    }
> +
> +    if (!mRequests.contains(request)) {
> +        request->deleteLater();
> +        return;
> +    }
> +
> +    if (state == QContactAbstractRequest::FinishedState) {
> +        request->deleteLater();
> +
> +        QString phoneNumber = mRequests.take(request);
> +        QContact contact;
> +        if (!request->contacts().isEmpty()) {
> +            contact = request->contacts().first();
> +        }
> +        populateInfo(phoneNumber, contact);
> +    } else if (state == QContactAbstractRequest::CanceledState) {
> +        request->deleteLater();
> +        mRequests.remove(request);
> +    }
> +
> +}
> +
> +void ContactMatcher::requestContactInfo(const QString &phoneNumber)
> +{
> +    // check if there is a request already going on for the given contact
> +    Q_FOREACH(const QString number, mRequests.values()) {
> +        if (PhoneUtils::comparePhoneNumbers(number, phoneNumber)) {
> +            // if so, just wait for it to finish
> +            return;
> +        }
> +    }
> +
> +    QContactFetchRequest *request = new QContactFetchRequest(this);
> +    QContactFetchHint hint;
> +    hint.setDetailTypesHint(QList<QContactDetail::DetailType>() << QContactDetail::TypeDisplayLabel
> +                                                                << QContactDetail::TypePhoneNumber
> +                                                                << QContactDetail::TypeAvatar);
> +    request->setFetchHint(hint);
> +    request->setFilter(QContactPhoneNumber::match(phoneNumber));
> +    request->setManager(mManager);
> +    connect(request,
> +            SIGNAL(stateChanged(QContactAbstractRequest::State)),
> +            SLOT(onRequestStateChanged(QContactAbstractRequest::State)));
> +
> +    mRequests[request] = phoneNumber;
> +    request->start();
> +}
> +
> +QVariantList ContactMatcher::toVariantList(const QList<int> &list)
> +{
> +    QVariantList variantList;
> +    Q_FOREACH(int value, list) {
> +        variantList << value;
> +    }
> +    return variantList;
> +}
> +
> +void ContactMatcher::populateInfo(const QString &phoneNumber, const QContact &contact)
> +{
> +    QVariantMap contactInfo;
> +    contactInfo[History::FieldPhoneNumber] = phoneNumber;
> +    if (!contact.isEmpty()) {
> +        contactInfo[History::FieldContactId] = contact.id().toString();
> +        contactInfo[History::FieldAlias] = QContactDisplayLabel(contact.detail(QContactDetail::TypeDisplayLabel)).label();
> +        contactInfo[History::FieldAvatar] = QContactAvatar(contact.detail(QContactDetail::TypeAvatar)).imageUrl().toString();
> +        //contactInfo[History::FieldAlias] = contact.dis

Leftover, fixed.

> +        Q_FOREACH(const QContactPhoneNumber number, contact.details(QContactDetail::TypePhoneNumber)) {
> +            if (PhoneUtils::comparePhoneNumbers(number.number(), phoneNumber)) {
> +                contactInfo[History::FieldPhoneSubTypes] = toVariantList(number.subTypes());
> +                contactInfo[History::FieldPhoneContexts] = toVariantList(number.contexts());
> +            }
> +        }
> +    }
> +    mContactMap[phoneNumber] = contactInfo;
> +    Q_EMIT contactInfoChanged(phoneNumber, contactInfo);
> +}
> 
> === added file 'Ubuntu/History/contactmatcher_p.h'
> --- Ubuntu/History/contactmatcher_p.h	1970-01-01 00:00:00 +0000
> +++ Ubuntu/History/contactmatcher_p.h	2014-09-09 13:18:46 +0000
> @@ -0,0 +1,63 @@
> +/*
> + * Copyright (C) 2014 Canonical, Ltd.
> + *
> + * Authors:
> + *  Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> + *
> + * This file is part of history-service.
> + *
> + * history-service is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; version 3.
> + *
> + * history-service is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + */
> +
> +#ifndef CONTACTMATCHER_P_H
> +#define CONTACTMATCHER_P_H
> +
> +#include <QObject>
> +#include <QVariantMap>
> +#include <QContactFetchRequest>
> +#include <QContactManager>
> +
> +using namespace QtContacts;
> +
> +typedef QMap<QString, QVariantMap> ContactMap;
> +class ContactMatcher : public QObject
> +{
> +    Q_OBJECT
> +public:
> +    static ContactMatcher *instance();
> +    QVariantMap contactInfo(const QString &phoneNumber);
> +    QVariantList contactInfo(const QStringList &numbers);
> +
> +Q_SIGNALS:
> +    void contactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo);
> +
> +protected Q_SLOTS:
> +    void onContactsAdded(QList<QContactId> ids);
> +    void onContactsChanged(QList<QContactId> ids);
> +    void onContactsRemoved(QList<QContactId> ids);
> +    void onRequestStateChanged(QContactAbstractRequest::State state);
> +
> +protected:
> +    void requestContactInfo(const QString &phoneNumber);
> +    QVariantList toVariantList(const QList<int> &list);
> +    void populateInfo(const QString &phoneNumber, const QContact &contact);
> +
> +private:
> +    explicit ContactMatcher(QObject *parent = 0);
> +
> +    ContactMap mContactMap;
> +    QMap<QContactFetchRequest*, QString> mRequests;
> +    QContactManager *mManager;
> +};
> +
> +#endif // CONTACTMATCHER_P_H
> 
> === modified file 'Ubuntu/History/historyeventmodel.cpp'
> --- Ubuntu/History/historyeventmodel.cpp	2014-08-18 19:12:14 +0000
> +++ Ubuntu/History/historyeventmodel.cpp	2014-09-09 13:18:46 +0000
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (C) 2013 Canonical, Ltd.
> + * Copyright (C) 2013-2014 Canonical, Ltd.
>   *
>   * Authors:
>   *  Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> @@ -29,8 +29,11 @@
>  #include "textevent.h"
>  #include "texteventattachment.h"
>  #include "historyqmltexteventattachment.h"
> +#include "phoneutils_p.h"
>  #include "thread.h"
> +#include "types.h"
>  #include "voiceevent.h"
> +#include "contactmatcher_p.h"
>  #include <QDebug>
>  #include <QTimerEvent>
>  #include <QDBusMetaType>
> @@ -38,7 +41,7 @@
>  HistoryEventModel::HistoryEventModel(QObject *parent) :
>      QAbstractListModel(parent), mCanFetchMore(true), mFilter(0),
>      mSort(new HistoryQmlSort(this)), mType(HistoryThreadModel::EventTypeText),
> -    mEventWritingTimer(0), mUpdateTimer(0)
> +    mMatchContacts(false), mEventWritingTimer(0), mUpdateTimer(0)
>  {
>      connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SIGNAL(countChanged()));
>      connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), this, SIGNAL(countChanged()));
> @@ -54,6 +57,7 @@
>      mRoles[TimestampRole] = "timestamp";
>      mRoles[DateRole] = "date";
>      mRoles[NewEventRole] = "newEvent";
> +    mRoles[PropertiesRole] = "properties";
>      mRoles[TextMessageRole] = "textMessage";
>      mRoles[TextMessageTypeRole] = "textMessageType";
>      mRoles[TextMessageStatusRole] = "textMessageStatus";
> @@ -63,6 +67,10 @@
>      mRoles[CallMissedRole] = "callMissed";
>      mRoles[CallDurationRole] = "callDuration";
>  
> +    connect(ContactMatcher::instance(),
> +            SIGNAL(contactInfoChanged(QString,QVariantMap)),
> +            SLOT(onContactInfoChanged(QString,QVariantMap)));
> +
>      // create the view and get some objects
>      triggerQueryUpdate();
>  }
> @@ -89,7 +97,6 @@
>  {
>      History::TextEvent textEvent;
>      History::VoiceEvent voiceEvent;
> -    History::Thread thread;
>  
>      switch (event.type()) {
>      case History::EventTypeText:
> @@ -110,7 +117,11 @@
>          result = event.threadId();
>          break;
>      case ParticipantsRole:
> -        result = event.participants();
> +        if (mMatchContacts) {
> +            result = ContactMatcher::instance()->contactInfo(event.participants());
> +        } else {
> +            result = event.participants();
> +        }
>          break;
>      case TypeRole:
>          result = event.type();
> @@ -130,6 +141,9 @@
>      case NewEventRole:
>          result = event.newEvent();
>          break;
> +    case PropertiesRole:
> +        result = event.properties();
> +        break;
>      case TextMessageRole:
>          if (!textEvent.isNull()) {
>              result = textEvent.message();
> @@ -274,6 +288,22 @@
>      triggerQueryUpdate();
>  }
>  
> +bool HistoryEventModel::matchContacts() const
> +{
> +    return mMatchContacts;
> +}
> +
> +void HistoryEventModel::setMatchContacts(bool value)
> +{
> +    mMatchContacts = value;
> +    Q_EMIT matchContactsChanged();
> +
> +    // mark all indexes as changed
> +    if (rowCount() > 0) {
> +        Q_EMIT dataChanged(index(0), index(rowCount()-1));
> +    }
> +}
> +
>  QString HistoryEventModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create)
>  {
>      if (participants.isEmpty()) {
> @@ -459,6 +489,34 @@
>      // should be handle internally in History::EventView?
>  }
>  
> +void HistoryEventModel::onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo)
> +{
> +    Q_UNUSED(contactInfo)
> +    if (!mMatchContacts) {
> +        return;
> +    }
> +
> +    QList<QModelIndex> changedIndexes;
> +    int count = rowCount();
> +    for (int i = 0; i < count; ++i) {
> +        // WARNING: do not use mEvents directly to verify which indexes to change as there is the
> +        // HistoryGroupedEventsModel which is based on this model and handles the items in a different way
> +        QModelIndex idx = index(i);
> +        QVariantMap eventProperties = idx.data(PropertiesRole).toMap();
> +        QStringList participants = eventProperties[History::FieldParticipants].toStringList();
> +        Q_FOREACH(const QString &participant, participants) {
> +            if (PhoneUtils::comparePhoneNumbers(participant, phoneNumber)) {
> +                changedIndexes << idx;
> +            }
> +        }
> +    }
> +
> +    // now emit the dataChanged signal to all changed indexes
> +    Q_FOREACH(const QModelIndex &idx, changedIndexes) {
> +        Q_EMIT dataChanged(idx, idx);
> +    }
> +}
> +
>  void HistoryEventModel::timerEvent(QTimerEvent *event)
>  {
>      if (event->timerId() == mEventWritingTimer) {
> 
> === modified file 'Ubuntu/History/historyeventmodel.h'
> --- Ubuntu/History/historyeventmodel.h	2014-08-18 19:12:14 +0000
> +++ Ubuntu/History/historyeventmodel.h	2014-09-09 13:18:46 +0000
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (C) 2013 Canonical, Ltd.
> + * Copyright (C) 2013-2014 Canonical, Ltd.
>   *
>   * Authors:
>   *  Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> @@ -33,6 +33,7 @@
>      Q_PROPERTY(HistoryQmlFilter *filter READ filter WRITE setFilter NOTIFY filterChanged)
>      Q_PROPERTY(HistoryQmlSort *sort READ sort WRITE setSort NOTIFY sortChanged)
>      Q_PROPERTY(HistoryThreadModel::EventType type READ type WRITE setType NOTIFY typeChanged)
> +    Q_PROPERTY(bool matchContacts READ matchContacts WRITE setMatchContacts NOTIFY matchContactsChanged)
>      Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged)
>      Q_ENUMS(Role)
>  public:
> @@ -46,6 +47,7 @@
>          TimestampRole,
>          DateRole,
>          NewEventRole,
> +        PropertiesRole,
>          TextMessageRole,
>          TextMessageTypeRole,
>          TextMessageStatusRole,
> @@ -77,6 +79,9 @@
>      HistoryThreadModel::EventType type() const;
>      void setType(HistoryThreadModel::EventType value);
>  
> +    bool matchContacts() const;
> +    void setMatchContacts(bool value);
> +
>      Q_INVOKABLE QString threadIdForParticipants(const QString &accountId,
>                                                  int eventType,
>                                                  const QStringList &participants,
> @@ -94,6 +99,7 @@
>      void filterChanged();
>      void sortChanged();
>      void typeChanged();
> +    void matchContactsChanged();
>      void canFetchMoreChanged();
>  
>  protected Q_SLOTS:
> @@ -102,6 +108,7 @@
>      virtual void onEventsAdded(const History::Events &events);
>      virtual void onEventsModified(const History::Events &events);
>      virtual void onEventsRemoved(const History::Events &events);
> +    void onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo);
>  
>  protected:
>      void timerEvent(QTimerEvent *event);
> @@ -114,6 +121,7 @@
>      HistoryQmlFilter *mFilter;
>      HistoryQmlSort *mSort;
>      HistoryThreadModel::EventType mType;
> +    bool mMatchContacts;
>      QHash<int, QByteArray> mRoles;
>      mutable QMap<History::TextEvent, QList<QVariant> > mAttachmentCache;
>      History::Events mEventWritingQueue;
> 
> === modified file 'Ubuntu/History/historythreadmodel.cpp'
> --- Ubuntu/History/historythreadmodel.cpp	2014-08-18 19:12:14 +0000
> +++ Ubuntu/History/historythreadmodel.cpp	2014-09-09 13:18:46 +0000
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (C) 2013 Canonical, Ltd.
> + * Copyright (C) 2013-2014 Canonical, Ltd.
>   *
>   * Authors:
>   *  Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> @@ -29,6 +29,8 @@
>  #include "texteventattachment.h"
>  #include "historyqmltexteventattachment.h"
>  #include "voiceevent.h"
> +#include "contactmatcher_p.h"
> +#include "phoneutils_p.h"
>  #include <QDebug>
>  #include <QTimerEvent>
>  
> @@ -36,7 +38,7 @@
>  
>  HistoryThreadModel::HistoryThreadModel(QObject *parent) :
>      QAbstractListModel(parent), mCanFetchMore(true), mFilter(0), mSort(0),
> -    mType(EventTypeText), mUpdateTimer(0)
> +    mType(EventTypeText), mMatchContacts(false), mUpdateTimer(0)
>  {
>      // configure the roles
>      mRoles[AccountIdRole] = "accountId";
> @@ -65,6 +67,9 @@
>      connect(this, SIGNAL(rowsInserted(QModelIndex,int,int)), SIGNAL(countChanged()));
>      connect(this, SIGNAL(rowsRemoved(QModelIndex,int,int)), SIGNAL(countChanged()));
>      connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
> +    connect(ContactMatcher::instance(),
> +            SIGNAL(contactInfoChanged(QString,QVariantMap)),
> +            SLOT(onContactInfoChanged(QString,QVariantMap)));
>  
>      // create the results view
>      triggerQueryUpdate();
> @@ -113,7 +118,11 @@
>          result = (int) thread.type();
>          break;
>      case ParticipantsRole:
> -        result = thread.participants();
> +        if (mMatchContacts) {
> +            result = ContactMatcher::instance()->contactInfo(thread.participants());
> +        } else {
> +            result = thread.participants();
> +        }
>          break;
>      case CountRole:
>          result = thread.count();
> @@ -292,6 +301,22 @@
>      triggerQueryUpdate();
>  }
>  
> +bool HistoryThreadModel::matchContacts() const
> +{
> +    return mMatchContacts;
> +}
> +
> +void HistoryThreadModel::setMatchContacts(bool value)
> +{
> +    mMatchContacts = value;
> +    Q_EMIT matchContactsChanged();
> +
> +    // mark all indexes as changed
> +    if (rowCount() > 0) {
> +        Q_EMIT dataChanged(index(0), index(rowCount()-1));
> +    }
> +}
> +
>  QString HistoryThreadModel::threadIdForParticipants(const QString &accountId, int eventType, const QStringList &participants, int matchFlags, bool create)
>  {
>      if (participants.isEmpty()) {
> @@ -439,3 +464,32 @@
>          updateQuery();
>      }
>  }
> +
> +
> +void HistoryThreadModel::onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo)
> +{
> +    Q_UNUSED(contactInfo)
> +    if (!mMatchContacts) {
> +        return;
> +    }
> +
> +    QList<QModelIndex> changedIndexes;
> +    int count = rowCount();
> +    for (int i = 0; i < count; ++i) {
> +        // WARNING: do not use mEvents directly to verify which indexes to change as there is the
> +        // HistoryGroupedEventsModel which is based on this model and handles the items in a different way
> +        QModelIndex idx = index(i);
> +        QVariantMap eventProperties = idx.data(PropertiesRole).toMap();
> +        QStringList participants = eventProperties[History::FieldParticipants].toStringList();
> +        Q_FOREACH(const QString &participant, participants) {
> +            if (PhoneUtils::comparePhoneNumbers(participant, phoneNumber)) {
> +                changedIndexes << idx;
> +            }
> +        }
> +    }
> +
> +    // now emit the dataChanged signal to all changed indexes
> +    Q_FOREACH(const QModelIndex &idx, changedIndexes) {
> +        Q_EMIT dataChanged(idx, idx);
> +    }
> +}
> 
> === modified file 'Ubuntu/History/historythreadmodel.h'
> --- Ubuntu/History/historythreadmodel.h	2014-08-18 19:12:14 +0000
> +++ Ubuntu/History/historythreadmodel.h	2014-09-09 13:18:46 +0000
> @@ -1,5 +1,5 @@
>  /*
> - * Copyright (C) 2013 Canonical, Ltd.
> + * Copyright (C) 2013-2014 Canonical, Ltd.
>   *
>   * Authors:
>   *  Gustavo Pichorim Boiko <gustavo.boiko at canonical.com>
> @@ -36,6 +36,7 @@
>      Q_PROPERTY(HistoryQmlFilter *filter READ filter WRITE setFilter NOTIFY filterChanged)
>      Q_PROPERTY(HistoryQmlSort *sort READ sort WRITE setSort NOTIFY sortChanged)
>      Q_PROPERTY(EventType type READ type WRITE setType NOTIFY typeChanged)
> +    Q_PROPERTY(bool matchContacts READ matchContacts WRITE setMatchContacts NOTIFY matchContactsChanged)
>      Q_PROPERTY(int count READ rowCount NOTIFY countChanged)
>      Q_PROPERTY(bool canFetchMore READ canFetchMore NOTIFY canFetchMoreChanged)
>      Q_ENUMS(EventType)
> @@ -109,6 +110,9 @@
>      EventType type() const;
>      void setType(EventType value);
>  
> +    bool matchContacts() const;
> +    void setMatchContacts(bool value);
> +
>      Q_INVOKABLE QString threadIdForParticipants(const QString &accountId,
>                                                  int eventType,
>                                                  const QStringList &participants,
> @@ -121,6 +125,7 @@
>      void filterChanged();
>      void sortChanged();
>      void typeChanged();
> +    void matchContactsChanged();
>      void countChanged();
>      void canFetchMoreChanged();
>  
> @@ -130,6 +135,7 @@
>      void onThreadsAdded(const History::Threads &threads);
>      void onThreadsModified(const History::Threads &threads);
>      void onThreadsRemoved(const History::Threads &threads);
> +    void onContactInfoChanged(const QString &phoneNumber, const QVariantMap &contactInfo);
>  
>  protected:
>      void timerEvent(QTimerEvent *event);
> @@ -141,6 +147,7 @@
>      HistoryQmlFilter *mFilter;
>      HistoryQmlSort *mSort;
>      EventType mType;
> +    bool mMatchContacts;
>      QHash<int, QByteArray> mRoles;
>      mutable QMap<History::TextEvent, QList<QVariant> > mAttachmentCache;
>      int mUpdateTimer;
> 
> === modified file 'debian/control'
> --- debian/control	2013-12-12 19:22:03 +0000
> +++ debian/control	2014-09-09 13:18:46 +0000
> @@ -17,6 +17,7 @@
>                 qt5-default,
>                 qtbase5-dev (>= 5.0),
>                 qtdeclarative5-dev (>= 5.0),
> +               qtpim5-dev (>= 5),

Fixed.

>                 sqlite3,
>                 telepathy-mission-control-5,
>  Standards-Version: 3.9.4
> 
> === modified file 'src/types.h'
> --- src/types.h	2014-08-16 22:11:30 +0000
> +++ src/types.h	2014-09-09 13:18:46 +0000
> @@ -140,6 +140,14 @@
>  static const char* FieldMatchFlags = "matchFlags";
>  static const char* FieldFilters = "filters";
>  
> +// contact matching stuff
> +static const char* FieldContactId = "contactId";
> +static const char* FieldAlias = "alias";
> +static const char* FieldAvatar = "avatar";
> +static const char* FieldPhoneNumber = "phoneNumber";
> +static const char* FieldPhoneSubTypes = "phoneSubTypes";
> +static const char* FieldPhoneContexts = "phoneContexts";
> +
>  }
>  
>  #endif // TYPES_H
> 


-- 
https://code.launchpad.net/~boiko/history-service/contact_aware_models/+merge/233860
Your team Ubuntu Phablet Team is subscribed to branch lp:history-service.



More information about the Ubuntu-reviews mailing list