seasidecache.h 19.1 KB
Newer Older
mvogt's avatar
mvogt committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
/*
 * Copyright (C) 2013 Jolla Mobile <andrew.den.exter@jollamobile.com>
 *
 * You may use this file under the terms of the BSD license as follows:
 *
 * "Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *   * Neither the name of Nemo Mobile nor the names of its contributors
 *     may be used to endorse or promote products derived from this
 *     software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
 */

#ifndef SEASIDECACHE_H
#define SEASIDECACHE_H

#include "contactcacheexport.h"
36
#include "cacheconfiguration.h"
37
#include "seasidenamegrouper.h"
mvogt's avatar
mvogt committed
38 39

#include <qtcontacts-extensions.h>
40
#include <QContactStatusFlags>
mvogt's avatar
mvogt committed
41 42 43 44 45 46 47 48 49 50 51 52

#include <QContact>
#include <QContactManager>
#include <QContactFetchRequest>
#include <QContactFetchByIdRequest>
#include <QContactRemoveRequest>
#include <QContactSaveRequest>
#include <QContactRelationshipFetchRequest>
#include <QContactRelationshipSaveRequest>
#include <QContactRelationshipRemoveRequest>
#include <QContactIdFilter>
#include <QContactIdFetchRequest>
53
#include <QContactName>
mvogt's avatar
mvogt committed
54

55
#include <QTranslator>
mvogt's avatar
mvogt committed
56
#include <QBasicTimer>
57
#include <QHash>
mvogt's avatar
mvogt committed
58 59 60 61 62
#include <QSet>

#include <QElapsedTimer>
#include <QAbstractListModel>

63 64
QTCONTACTS_USE_NAMESPACE

mvogt's avatar
mvogt committed
65 66 67 68 69 70
class CONTACTCACHE_EXPORT SeasideNameGroupChangeListener
{
public:
    SeasideNameGroupChangeListener() {}
    ~SeasideNameGroupChangeListener() {}

71
    virtual void nameGroupsUpdated(const QHash<QString, QSet<quint32> > &groups) = 0;
mvogt's avatar
mvogt committed
72 73 74 75 76 77 78 79 80 81 82 83 84 85
};

class CONTACTCACHE_EXPORT SeasideCache : public QObject
{
    Q_OBJECT
public:
    enum FilterType {
        FilterNone,
        FilterAll,
        FilterFavorites,
        FilterOnline,
        FilterTypesCount
    };

86 87 88 89
    enum FetchDataType {
        FetchNone = 0,
        FetchAccountUri = (1 << 0),
        FetchPhoneNumber = (1 << 1),
90
        FetchEmailAddress = (1 << 2),
91 92 93 94 95
        FetchOrganization = (1 << 3),
        FetchTypesMask = (FetchAccountUri |
                          FetchPhoneNumber |
                          FetchEmailAddress |
                          FetchOrganization)
96 97
    };

mvogt's avatar
mvogt committed
98
    enum DisplayLabelOrder {
99 100
        FirstNameFirst = CacheConfiguration::FirstNameFirst,
        LastNameFirst = CacheConfiguration::LastNameFirst
mvogt's avatar
mvogt committed
101 102 103 104
    };

    enum ContactState {
        ContactAbsent,
105
        ContactPartial,
mvogt's avatar
mvogt committed
106
        ContactRequested,
107
        ContactComplete
mvogt's avatar
mvogt committed
108 109
    };

110 111 112 113 114
    enum {
        // Must be after the highest bit used in QContactStatusFlags::Flag
        HasValidOnlineAccount = (QContactStatusFlags::IsOnline << 1)
    };

mvogt's avatar
mvogt committed
115 116 117 118 119 120
    struct ItemData
    {
        virtual ~ItemData() {}

        virtual void displayLabelOrderChanged(DisplayLabelOrder order) = 0;

121
        virtual void updateContact(const QContact &newContact, QContact *oldContact, ContactState state) = 0;
mvogt's avatar
mvogt committed
122 123 124 125 126 127 128 129

        virtual void constituentsFetched(const QList<int> &ids) = 0;
        virtual void mergeCandidatesFetched(const QList<int> &ids) = 0;
        virtual void aggregationOperationCompleted() = 0;

        virtual QList<int> constituents() const = 0;
    };

130 131
    struct CacheItem;
    struct ItemListener
mvogt's avatar
mvogt committed
132
    {
133 134
        ItemListener() : next(0), key(0) {}
        virtual ~ItemListener() {}
mvogt's avatar
mvogt committed
135

136 137 138 139 140
        virtual void itemUpdated(CacheItem *item) = 0;
        virtual void itemAboutToBeRemoved(CacheItem *item) = 0;

        ItemListener *next;
        void *key;
mvogt's avatar
mvogt committed
141 142 143 144
    };

    struct CacheItem
    {
145
        CacheItem() : itemData(0), iid(0), statusFlags(0), contactState(ContactAbsent), listeners(0) {}
146
        CacheItem(const QContact &contact)
147 148
            : contact(contact), itemData(0), iid(internalId(contact)),
              statusFlags(contact.detail<QContactStatusFlags>().flagsValue()), contactState(ContactAbsent), listeners(0) {}
mvogt's avatar
mvogt committed
149

150
        QContactId apiId() const { return SeasideCache::apiId(contact); }
mvogt's avatar
mvogt committed
151

152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
        ItemListener *appendListener(ItemListener *listener, void *key)
        {
            if (listeners) {
                ItemListener *existing(listeners);
                while (existing->next) {
                    existing = existing->next;
                }
                existing->next = listener;
            } else {
                listeners = listener;
            }

            listener->next = 0;
            listener->key = key;
            return listener;
        }

169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
        bool removeListener(ItemListener *listener)
        {
            if (listeners) {
                ItemListener *existing(listeners);
                ItemListener **previous = &listeners;

                while (existing) {
                    if (existing == listener) {
                        *previous = listener->next;
                        return true;
                    }
                    previous = &existing->next;
                    existing = existing->next;
                }
            }

            return false;
        }

188 189
        ItemListener *listener(void *key)
        {
190 191 192
            ItemListener *existing(listeners);
            while (existing && (existing->key != key) && (existing->next)) {
                existing = existing->next;
193
            }
194
            return (existing && (existing->key == key)) ? existing : 0;
195 196
        }

mvogt's avatar
mvogt committed
197 198
        QContact contact;
        ItemData *itemData;
199
        quint32 iid;
200
        quint64 statusFlags;
mvogt's avatar
mvogt committed
201
        ContactState contactState;
202
        ItemListener *listeners;
203
        QString nameGroup;
204
        QString displayLabel;
mvogt's avatar
mvogt committed
205 206 207 208
    };

    struct ContactLinkRequest
    {
209
        ContactLinkRequest(const QContactId &id) : contactId(id), constituentsFetched(false) {}
mvogt's avatar
mvogt committed
210 211
        ContactLinkRequest(const ContactLinkRequest &req) : contactId(req.contactId), constituentsFetched(req.constituentsFetched) {}

212
        QContactId contactId;
mvogt's avatar
mvogt committed
213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
        bool constituentsFetched;
    };

    class ListModel : public QAbstractListModel
    {
    public:
        ListModel(QObject *parent = 0) : QAbstractListModel(parent) {}
        virtual ~ListModel() {}

        virtual void sourceAboutToRemoveItems(int begin, int end) = 0;
        virtual void sourceItemsRemoved() = 0;

        virtual void sourceAboutToInsertItems(int begin, int end) = 0;
        virtual void sourceItemsInserted(int begin, int end) = 0;

        virtual void sourceDataChanged(int begin, int end) = 0;

230 231
        virtual void sourceItemsChanged() = 0;

mvogt's avatar
mvogt committed
232 233
        virtual void makePopulated() = 0;
        virtual void updateDisplayLabelOrder() = 0;
234
        virtual void updateSortProperty() = 0;
235
        virtual void updateGroupProperty() = 0;
mvogt's avatar
mvogt committed
236 237
    };

238 239 240 241
    struct ResolveListener
    {
        virtual ~ResolveListener() {}

242 243 244 245 246 247 248 249 250
        virtual void addressResolved(const QString &first, const QString &second, CacheItem *item) = 0;
    };

    struct ChangeListener
    {
        virtual ~ChangeListener() {}

        virtual void itemUpdated(CacheItem *item) = 0;
        virtual void itemAboutToBeRemoved(CacheItem *item) = 0;
251 252
    };

mvogt's avatar
mvogt committed
253
    static SeasideCache *instance();
254
    static QContactManager *manager();
mvogt's avatar
mvogt committed
255

256 257
    static QContactId apiId(const QContact &contact);
    static QContactId apiId(quint32 iid);
mvogt's avatar
mvogt committed
258

259
    static bool validId(const QContactId &id);
mvogt's avatar
mvogt committed
260 261 262 263

    static quint32 internalId(const QContact &contact);
    static quint32 internalId(const QContactId &id);

264
    static void registerModel(ListModel *model, FilterType type, FetchDataType requiredTypes = FetchNone, FetchDataType extraTypes = FetchNone);
mvogt's avatar
mvogt committed
265 266 267 268 269 270 271 272
    static void unregisterModel(ListModel *model);

    static void registerUser(QObject *user);
    static void unregisterUser(QObject *user);

    static void registerNameGroupChangeListener(SeasideNameGroupChangeListener *listener);
    static void unregisterNameGroupChangeListener(SeasideNameGroupChangeListener *listener);

273 274 275 276 277
    static void registerChangeListener(ChangeListener *listener);
    static void unregisterChangeListener(ChangeListener *listener);

    static void unregisterResolveListener(ResolveListener *listener);

278 279
    static void setNameGrouper(SeasideNameGrouper *grouper);

mvogt's avatar
mvogt committed
280
    static DisplayLabelOrder displayLabelOrder();
281
    static QString sortProperty();
282
    static QString groupProperty();
mvogt's avatar
mvogt committed
283 284 285

    static int contactId(const QContact &contact);

286
    static CacheItem *existingItem(const QContactId &id);
287
    static CacheItem *existingItem(quint32 iid);
288
    static CacheItem *itemById(const QContactId &id, bool requireComplete = true);
289
    static CacheItem *itemById(int id, bool requireComplete = true);
290 291
    static QContactId selfContactId();
    static QContact contactById(const QContactId &id);
292 293

    static void ensureCompletion(CacheItem *cacheItem);
294
    static void refreshContact(CacheItem *cacheItem);
295

296 297
    static QString nameGroup(const CacheItem *cacheItem);
    static QString determineNameGroup(const CacheItem *cacheItem);
298

299 300
    static QStringList allNameGroups();
    static QHash<QString, QSet<quint32> > nameGroupMembers();
mvogt's avatar
mvogt committed
301

302 303 304 305 306 307 308 309
    static CacheItem *itemByPhoneNumber(const QString &number, bool requireComplete = true);
    static CacheItem *itemByEmailAddress(const QString &address, bool requireComplete = true);
    static CacheItem *itemByOnlineAccount(const QString &localUid, const QString &remoteUid, bool requireComplete = true);

    static CacheItem *resolvePhoneNumber(ResolveListener *listener, const QString &number, bool requireComplete = true);
    static CacheItem *resolveEmailAddress(ResolveListener *listener, const QString &address, bool requireComplete = true);
    static CacheItem *resolveOnlineAccount(ResolveListener *listener, const QString &localUid, const QString &remoteUid, bool requireComplete = true);

mvogt's avatar
mvogt committed
310
    static bool saveContact(const QContact &contact);
311
    static bool removeContact(const QContact &contact);
mvogt's avatar
mvogt committed
312 313 314 315

    static void aggregateContacts(const QContact &contact1, const QContact &contact2);
    static void disaggregateContacts(const QContact &contact1, const QContact &contact2);

316 317
    static bool fetchConstituents(const QContact &contact);
    static bool fetchMergeCandidates(const QContact &contact);
mvogt's avatar
mvogt committed
318 319 320 321

    static int importContacts(const QString &path);
    static QString exportContacts();

322
    static const QList<quint32> *contacts(FilterType filterType);
mvogt's avatar
mvogt committed
323 324
    static bool isPopulated(FilterType filterType);

325 326 327
    static QString primaryName(const QString &firstName, const QString &lastName);
    static QString secondaryName(const QString &firstName, const QString &lastName);

328
    static void decomposeDisplayLabel(const QString &formattedDisplayLabel, QContactName *nameDetail);
mvogt's avatar
mvogt committed
329 330
    static QString generateDisplayLabel(const QContact &contact, DisplayLabelOrder order = FirstNameFirst);
    static QString generateDisplayLabelFromNonNameDetails(const QContact &contact);
331
    static QUrl filteredAvatarUrl(const QContact &contact, const QStringList &metadataFragments = QStringList());
mvogt's avatar
mvogt committed
332

333 334
    static QString normalizePhoneNumber(const QString &input, bool validate = false);
    static QString minimizePhoneNumber(const QString &input, bool validate = false);
335

mvogt's avatar
mvogt committed
336 337 338
    bool event(QEvent *event);

    // For synchronizeLists()
339
    int insertRange(int index, int count, const QList<quint32> &source, int sourceIndex) { return insertRange(m_syncFilter, index, count, source, sourceIndex); }
340
    int removeRange(int index, int count) { removeRange(m_syncFilter, index, count); return 0; }
mvogt's avatar
mvogt committed
341 342 343

protected:
    void timerEvent(QTimerEvent *event);
344
    void setSortOrder(const QString &property);
345
    void startRequest(bool *idleProcessing);
mvogt's avatar
mvogt committed
346 347 348 349 350 351

private slots:
    void contactsAvailable();
    void contactIdsAvailable();
    void relationshipsAvailable();
    void requestStateChanged(QContactAbstractRequest::State state);
352
    void addressRequestStateChanged(QContactAbstractRequest::State state);
mvogt's avatar
mvogt committed
353 354 355
    void updateContacts();
    void contactsAdded(const QList<QContactId> &contactIds);
    void contactsChanged(const QList<QContactId> &contactIds);
356
    void contactsPresenceChanged(const QList<QContactId> &contactIds);
mvogt's avatar
mvogt committed
357
    void contactsRemoved(const QList<QContactId> &contactIds);
358 359 360
    void displayLabelOrderChanged(CacheConfiguration::DisplayLabelOrder order);
    void sortPropertyChanged(const QString &sortProperty);
    void groupPropertyChanged(const QString &groupProperty);
361
    void displayStatusChanged(const QString &);
mvogt's avatar
mvogt committed
362 363

private:
364 365 366 367 368
    enum PopulateProgress {
        Unpopulated,
        FetchFavorites,
        FetchMetadata,
        FetchOnline,
369
        Populated
370 371
    };

mvogt's avatar
mvogt committed
372 373 374 375 376
    SeasideCache();
    ~SeasideCache();

    static void checkForExpiry();

377
    void keepPopulated(quint32 requiredTypes, quint32 extraTypes);
mvogt's avatar
mvogt committed
378 379

    void requestUpdate();
380
    void appendContacts(const QList<QContact> &contacts, FilterType filterType, bool partialFetch, const QSet<QContactDetail::DetailType> &queryDetailTypes);
mvogt's avatar
mvogt committed
381
    void fetchContacts();
382
    void updateContacts(const QList<QContactId> &contactIds, QList<QContactId> *updateList);
383
    void applyPendingContactUpdates();
384
    void applyContactUpdates(const QList<QContact> &contacts, const QSet<QContactDetail::DetailType> &queryDetailTypes);
mvogt's avatar
mvogt committed
385

386
    void resolveUnknownAddresses(const QString &first, const QString &second, CacheItem *item);
387
    bool updateContactIndexing(const QContact &oldContact, const QContact &contact, quint32 iid, const QSet<QContactDetail::DetailType> &queryDetailTypes, CacheItem *item);
388
    void updateCache(CacheItem *item, const QContact &contact, bool partialFetch, bool initialInsert);
389
    void reportItemUpdated(CacheItem *item);
390

mvogt's avatar
mvogt committed
391
    void removeRange(FilterType filter, int index, int count);
392
    int insertRange(FilterType filter, int index, int count, const QList<quint32> &queryIds, int queryIndex);
mvogt's avatar
mvogt committed
393

394 395 396
    void contactDataChanged(quint32 iid);
    void contactDataChanged(quint32 iid, FilterType filter);
    void removeContactData(quint32 iid, FilterType filter);
mvogt's avatar
mvogt committed
397 398
    void makePopulated(FilterType filter);

399 400 401
    void addToContactNameGroup(quint32 iid, const QString &group, QSet<QString> *modifiedGroups = 0);
    void removeFromContactNameGroup(quint32 iid, const QString &group, QSet<QString> *modifiedGroups = 0);
    void notifyNameGroupsChanged(const QSet<QString> &groups);
mvogt's avatar
mvogt committed
402

403 404
    void updateConstituentAggregations(const QContactId &contactId);
    void completeContactAggregation(const QContactId &contact1Id, const QContactId &contact2Id);
mvogt's avatar
mvogt committed
405

406 407
    void resolveAddress(ResolveListener *listener, const QString &first, const QString &second, bool requireComplete);

408 409
    CacheItem *itemMatchingPhoneNumber(const QString &number, const QString &normalized, bool requireComplete);

410 411
    int contactIndex(quint32 iid, FilterType filter);

412
    static QContactRelationship makeRelationship(const QString &type, const QContactId &id1, const QContactId &id2);
mvogt's avatar
mvogt committed
413 414
    static QContactRelationship makeRelationship(const QString &type, const QContact &contact1, const QContact &contact2);

415 416
    QList<quint32> m_contacts[FilterTypesCount];

mvogt's avatar
mvogt committed
417 418 419
    QBasicTimer m_expiryTimer;
    QBasicTimer m_fetchTimer;
    QHash<quint32, CacheItem> m_people;
420
    QMultiHash<QString, quint32> m_phoneNumberIds;
mvogt's avatar
mvogt committed
421
    QHash<QString, quint32> m_emailAddressIds;
422
    QHash<QPair<QString, QString>, quint32> m_onlineAccountIds;
423
    QHash<QContactId, QContact> m_contactsToSave;
424
    QHash<QString, QSet<quint32> > m_contactNameGroups;
mvogt's avatar
mvogt committed
425
    QList<QContact> m_contactsToCreate;
426 427 428 429 430 431
    QHash<FilterType, QPair<QSet<QContactDetail::DetailType>, QList<QContact> > > m_contactsToAppend;
    QList<QPair<QSet<QContactDetail::DetailType>, QList<QContact> > > m_contactsToUpdate;
    QList<QContactId> m_contactsToRemove;
    QList<QContactId> m_changedContacts;
    QList<QContactId> m_presenceChangedContacts;
    QSet<QContactId> m_aggregatedContacts;
mvogt's avatar
mvogt committed
432 433
    QList<QContactId> m_contactsToFetchConstituents;
    QList<QContactId> m_contactsToFetchCandidates;
434
    QList<QContactId> m_contactsToLinkTo;
mvogt's avatar
mvogt committed
435 436 437
    QList<QPair<ContactLinkRequest, ContactLinkRequest> > m_contactPairsToLink;
    QList<QContactRelationship> m_relationshipsToSave;
    QList<QContactRelationship> m_relationshipsToRemove;
438
    QScopedPointer<SeasideNameGrouper> m_nameGrouper;
mvogt's avatar
mvogt committed
439
    QList<SeasideNameGroupChangeListener*> m_nameGroupChangeListeners;
440
    QList<ChangeListener*> m_changeListeners;
mvogt's avatar
mvogt committed
441 442
    QList<ListModel *> m_models[FilterTypesCount];
    QSet<QObject *> m_users;
443
    QHash<QContactId,int> m_expiredContacts;
mvogt's avatar
mvogt committed
444 445 446 447 448 449 450 451
    QContactFetchRequest m_fetchRequest;
    QContactFetchByIdRequest m_fetchByIdRequest;
    QContactIdFetchRequest m_contactIdRequest;
    QContactRelationshipFetchRequest m_relationshipsFetchRequest;
    QContactRemoveRequest m_removeRequest;
    QContactSaveRequest m_saveRequest;
    QContactRelationshipSaveRequest m_relationshipSaveRequest;
    QContactRelationshipRemoveRequest m_relationshipRemoveRequest;
452 453
    QList<QContactSortOrder> m_sortOrder;
    QList<QContactSortOrder> m_onlineSortOrder;
454
    FilterType m_syncFilter;
mvogt's avatar
mvogt committed
455 456 457
    int m_populated;
    int m_cacheIndex;
    int m_queryIndex;
458 459
    int m_fetchProcessedCount;
    int m_fetchByIdProcessedCount;
mvogt's avatar
mvogt committed
460
    DisplayLabelOrder m_displayLabelOrder;
461
    QString m_sortProperty;
462
    QString m_groupProperty;
mvogt's avatar
mvogt committed
463
    bool m_keepPopulated;
464
    PopulateProgress m_populateProgress;
465
    bool m_populating; // true if current m_fetchRequest makes progress
466
    quint32 m_fetchTypes;
467 468
    quint32 m_extraFetchTypes;
    quint32 m_dataTypesFetched;
mvogt's avatar
mvogt committed
469 470 471
    bool m_updatesPending;
    bool m_refreshRequired;
    bool m_contactsUpdated;
472
    bool m_displayOff;
473 474
    QSet<QContactId> m_constituentIds;
    QSet<QContactId> m_candidateIds;
mvogt's avatar
mvogt committed
475

476 477 478
    struct ResolveData {
        QString first;
        QString second;
479
        QString compare; // only used in m_unknownAddresses
480 481 482
        bool requireComplete;
        ResolveListener *listener;
    };
483
    QHash<QContactFetchRequest *, ResolveData> m_resolveAddresses;
484
    QSet<ResolveData> m_pendingResolve; // these have active requests already
485
    QList<ResolveData> m_unknownResolveAddresses;
486
    QList<ResolveData> m_unknownAddresses;
487
    QSet<QString> m_resolvedPhoneNumbers;
488

mvogt's avatar
mvogt committed
489 490 491 492
    QElapsedTimer m_timer;
    QElapsedTimer m_fetchPostponed;

    static SeasideCache *instancePtr;
493
    static int contactNameGroupCount;
494
    static QStringList allContactNameGroups;
495 496
    static QTranslator *engEnTranslator;
    static QTranslator *translator;
497 498 499

    friend bool operator==(const SeasideCache::ResolveData &lhs, const SeasideCache::ResolveData &rhs);
    friend uint qHash(const SeasideCache::ResolveData &key, uint seed);
mvogt's avatar
mvogt committed
500 501
};

502 503 504
bool operator==(const SeasideCache::ResolveData &lhs, const SeasideCache::ResolveData &rhs);
uint qHash(const SeasideCache::ResolveData &key, uint seed = 0);

mvogt's avatar
mvogt committed
505
#endif