Skip to content
This repository has been archived by the owner on Sep 4, 2021. It is now read-only.

Commit

Permalink
Browse files Browse the repository at this point in the history
[libcontacts] Update changed contacts progressively
Update contacts in small batches to prevent blocking the UI thread.
  • Loading branch information
matthewvogt committed Dec 9, 2013
1 parent 380fa72 commit 7ef500e
Show file tree
Hide file tree
Showing 2 changed files with 92 additions and 39 deletions.
128 changes: 89 additions & 39 deletions src/seasidecache.cpp
Expand Up @@ -1531,6 +1531,12 @@ bool SeasideCache::event(QEvent *event)
m_contactIdRequest.setFilter(favoriteFilter());
m_contactIdRequest.setSorting(m_sortOrder);
m_contactIdRequest.start();
} else if (!m_contactsToUpdate.isEmpty()) {
applyPendingContactUpdates();
if (!m_contactsToUpdate.isEmpty()) {
// Send another event to trigger further processing
QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest));
}
} else {
m_updatesPending = false;

Expand Down Expand Up @@ -1912,6 +1918,9 @@ void SeasideCache::contactsAvailable()
fetchHint = m_fetchRequest.fetchHint();
}

if (contacts.isEmpty())
return;

QSet<DetailTypeId> queryDetailTypes;
foreach (const DetailTypeId &typeId, detailTypesHint(fetchHint)) {
queryDetailTypes.insert(typeId);
Expand All @@ -1925,60 +1934,101 @@ void SeasideCache::contactsAvailable()
: FilterOnline));
appendContacts(contacts, type, partialFetch, queryDetailTypes);
} else {
// An update.
QSet<QString> modifiedGroups;
if (m_activeResolve) {
// Process these results immediately
applyContactUpdates(contacts, partialFetch, queryDetailTypes);
} else {
// Add these contacts to the list to be progressively appended
QList<QPair<QSet<DetailTypeId>, QList<QContact> > >::iterator it = m_contactsToUpdate.begin(), end = m_contactsToUpdate.end();
for ( ; it != end; ++it) {
if ((*it).first == queryDetailTypes) {
(*it).second.append(contacts);
break;
}
}
if (it == end) {
m_contactsToUpdate.append(qMakePair(queryDetailTypes, contacts));
}

foreach (QContact contact, contacts) {
quint32 iid = internalId(contact);
requestUpdate();
}
}
}

QString oldNameGroup;
QString oldDisplayLabel;
void SeasideCache::applyPendingContactUpdates()
{
const int updateBatchSize = 3;

CacheItem *item = existingItem(iid);
if (!item) {
// We haven't seen this contact before
item = &(m_people[iid]);
item->iid = iid;
} else {
oldNameGroup = item->nameGroup;
oldDisplayLabel = item->displayLabel;
QList<QPair<QSet<DetailTypeId>, QList<QContact> > >::iterator it = m_contactsToUpdate.begin();

if (partialFetch) {
// Update our new instance with any details not returned by the current query
updateDetailsFromCache(contact, item, queryDetailTypes);
}
}
QSet<DetailTypeId> &detailTypes((*it).first);
const bool partialFetch = !detailTypes.isEmpty();

bool roleDataChanged = false;
// Update a small number of retrieved contacts
QList<QContact> &updatedContacts((*it).second);
if (updatedContacts.count() > updateBatchSize) {
applyContactUpdates(updatedContacts.mid(0, updateBatchSize), partialFetch, detailTypes);
updatedContacts = updatedContacts.mid(updateBatchSize);
} else {
applyContactUpdates(updatedContacts, partialFetch, detailTypes);
m_contactsToUpdate.erase(it);
}
}

// This is a simplification of reality, should we test more changes?
if (!partialFetch || queryDetailTypes.contains(detailType<QContactAvatar>())) {
roleDataChanged |= (contact.details<QContactAvatar>() != item->contact.details<QContactAvatar>());
}
if (!partialFetch || queryDetailTypes.contains(detailType<QContactGlobalPresence>())) {
roleDataChanged |= (contact.detail<QContactGlobalPresence>() != item->contact.detail<QContactGlobalPresence>());
}
void SeasideCache::applyContactUpdates(const QList<QContact> &contacts, bool partialFetch, const QSet<DetailTypeId> &queryDetailTypes)
{
QSet<QString> modifiedGroups;

roleDataChanged |= updateContactIndexing(item->contact, contact, iid, queryDetailTypes, item);
foreach (QContact contact, contacts) {
quint32 iid = internalId(contact);

updateCache(item, contact, partialFetch);
roleDataChanged |= (item->displayLabel != oldDisplayLabel);
QString oldNameGroup;
QString oldDisplayLabel;

// do this even if !roleDataChanged as name groups are affected by other display label changes
if (item->nameGroup != oldNameGroup) {
if (!ignoreContactForNameGroups(item->contact)) {
addToContactNameGroup(item->iid, item->nameGroup, &modifiedGroups);
removeFromContactNameGroup(item->iid, oldNameGroup, &modifiedGroups);
}
CacheItem *item = existingItem(iid);
if (!item) {
// We haven't seen this contact before
item = &(m_people[iid]);
item->iid = iid;
} else {
oldNameGroup = item->nameGroup;
oldDisplayLabel = item->displayLabel;

if (partialFetch) {
// Update our new instance with any details not returned by the current query
updateDetailsFromCache(contact, item, queryDetailTypes);
}
}

bool roleDataChanged = false;

if (roleDataChanged) {
instancePtr->contactDataChanged(item->iid);
// This is a simplification of reality, should we test more changes?
if (!partialFetch || queryDetailTypes.contains(detailType<QContactAvatar>())) {
roleDataChanged |= (contact.details<QContactAvatar>() != item->contact.details<QContactAvatar>());
}
if (!partialFetch || queryDetailTypes.contains(detailType<QContactGlobalPresence>())) {
roleDataChanged |= (contact.detail<QContactGlobalPresence>() != item->contact.detail<QContactGlobalPresence>());
}

roleDataChanged |= updateContactIndexing(item->contact, contact, iid, queryDetailTypes, item);

updateCache(item, contact, partialFetch);
roleDataChanged |= (item->displayLabel != oldDisplayLabel);

// do this even if !roleDataChanged as name groups are affected by other display label changes
if (item->nameGroup != oldNameGroup) {
if (!ignoreContactForNameGroups(item->contact)) {
addToContactNameGroup(item->iid, item->nameGroup, &modifiedGroups);
removeFromContactNameGroup(item->iid, oldNameGroup, &modifiedGroups);
}
}

notifyNameGroupsChanged(modifiedGroups);
if (roleDataChanged) {
instancePtr->contactDataChanged(item->iid);
}
}

notifyNameGroupsChanged(modifiedGroups);
}

void SeasideCache::addToContactNameGroup(quint32 iid, const QString &group, QSet<QString> *modifiedGroups)
Expand Down
3 changes: 3 additions & 0 deletions src/seasidecache.h
Expand Up @@ -395,6 +395,8 @@ private slots:
void appendContacts(const QList<QContact> &contacts, FilterType filterType, bool partialFetch, const QSet<DetailTypeId> &queryDetailTypes);
void fetchContacts();
void updateContacts(const QList<ContactIdType> &contactIds);
void applyPendingContactUpdates();
void applyContactUpdates(const QList<QContact> &contacts, bool partialFetch, const QSet<DetailTypeId> &queryDetailTypes);

void resolveUnknownAddresses(const QString &first, const QString &second, CacheItem *item);
bool updateContactIndexing(const QContact &oldContact, const QContact &contact, quint32 iid, const QSet<DetailTypeId> &queryDetailTypes, CacheItem *item);
Expand Down Expand Up @@ -435,6 +437,7 @@ private slots:
QHash<ContactIdType, QContact> m_contactsToSave;
QHash<QString, QSet<quint32> > m_contactNameGroups;
QList<QContact> m_contactsToCreate;
QList<QPair<QSet<DetailTypeId>, QList<QContact> > > m_contactsToUpdate;
QList<ContactIdType> m_contactsToRemove;
QList<ContactIdType> m_changedContacts;
QSet<ContactIdType> m_aggregatedContacts;
Expand Down

0 comments on commit 7ef500e

Please sign in to comment.