seasidecache.h 19 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 <QBasicTimer>
56
#include <QHash>
mvogt's avatar
mvogt committed
57 58 59 60 61
#include <QSet>

#include <QElapsedTimer>
#include <QAbstractListModel>

62 63
QTCONTACTS_USE_NAMESPACE

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

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

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

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

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

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

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

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

        virtual void displayLabelOrderChanged(DisplayLabelOrder order) = 0;

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

        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;
    };

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

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

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

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

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

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
        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;
        }

168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186
        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;
        }

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

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

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

211
        QContactId contactId;
mvogt's avatar
mvogt committed
212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
        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;

229 230
        virtual void sourceItemsChanged() = 0;

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

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

241 242 243 244 245 246 247 248 249
        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;
250 251
    };

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

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

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

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

263
    static void registerModel(ListModel *model, FilterType type, FetchDataType requiredTypes = FetchNone, FetchDataType extraTypes = FetchNone);
mvogt's avatar
mvogt committed
264 265 266 267 268 269 270 271
    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);

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

    static void unregisterResolveListener(ResolveListener *listener);

277 278
    static void setNameGrouper(SeasideNameGrouper *grouper);

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

    static int contactId(const QContact &contact);

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

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

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

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

301 302 303 304 305 306 307 308
    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
309
    static bool saveContact(const QContact &contact);
310
    static bool removeContact(const QContact &contact);
mvogt's avatar
mvogt committed
311 312 313 314

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

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

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

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

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

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

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

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

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

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

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

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

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

    static void checkForExpiry();

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

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

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

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

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

398 399 400
    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
401

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

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

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

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

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

414 415
    QList<quint32> m_contacts[FilterTypesCount];

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

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

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

    static SeasideCache *instancePtr;
492
    static int contactNameGroupCount;
493
    static QStringList allContactNameGroups;
494 495 496

    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
497 498
};

499 500 501
bool operator==(const SeasideCache::ResolveData &lhs, const SeasideCache::ResolveData &rhs);
uint qHash(const SeasideCache::ResolveData &key, uint seed = 0);

mvogt's avatar
mvogt committed
502
#endif