Skip to content

Commit

Permalink
Merged in chriadam/base-sociald/contacts-buteo (pull request #27)
Browse files Browse the repository at this point in the history
[sociald] Implement Facebook Friend sync.  Contributes to JB#6005
  • Loading branch information
Chris Adams committed Jul 26, 2013
2 parents b204a48 + 233c13d commit 4fdca83
Show file tree
Hide file tree
Showing 9 changed files with 1,272 additions and 6 deletions.
2 changes: 1 addition & 1 deletion rpm/sociald.spec
@@ -1,6 +1,6 @@
Name: sociald
Summary: Syncs device data from social services
Version: 0.0.16
Version: 0.0.17
Release: 1
Group: System/Applications
License: TBD
Expand Down
3 changes: 3 additions & 0 deletions src/constants_p.h
Expand Up @@ -9,9 +9,12 @@
#define SOCIALD_CONSTANTS_P_H

#include <QContactName>
#include <QContactAvatar>

BEGIN_CONTACTS_NAMESPACE
// some custom fields supported by qtcontacts-sqlite.
static const int QContactName__FieldCustomLabel = (QContactName::FieldSuffix+1);
static const int QContactAvatar__FieldAvatarMetadata = (QContactAvatar::FieldVideoUrl+1);
END_CONTACTS_NAMESPACE

#endif
2 changes: 2 additions & 0 deletions src/facebook/facebook.pri
Expand Up @@ -2,12 +2,14 @@ INCLUDEPATH += . ..

HEADERS += \
$$PWD/facebookdatatypesyncadaptor.h \
$$PWD/facebookcontactsyncadaptor.h \
$$PWD/facebookimagesyncadaptor.h \
$$PWD/facebooknotificationsyncadaptor.h \
$$PWD/facebookpostsyncadaptor.h

SOURCES += \
$$PWD/facebookdatatypesyncadaptor.cpp \
$$PWD/facebookcontactsyncadaptor.cpp \
$$PWD/facebookimagesyncadaptor.cpp \
$$PWD/facebooknotificationsyncadaptor.cpp \
$$PWD/facebookpostsyncadaptor.cpp
Expand Down
1,131 changes: 1,131 additions & 0 deletions src/facebook/facebookcontactsyncadaptor.cpp

Large diffs are not rendered by default.

100 changes: 100 additions & 0 deletions src/facebook/facebookcontactsyncadaptor.h
@@ -0,0 +1,100 @@
/****************************************************************************
**
** Copyright (C) 2013 Jolla Ltd.
** Contact: Chris Adams <chris.adams@jollamobile.com>
**
****************************************************************************/

#ifndef FACEBOOKCONTACTSYNCADAPTOR_H
#define FACEBOOKCONTACTSYNCADAPTOR_H

#include "facebookdatatypesyncadaptor.h"

#include <QtCore/QObject>
#include <QtCore/QString>
#include <QtCore/QDateTime>
#include <QtCore/QVariantMap>
#include <QtCore/QList>
#include <QtCore/QStringList>
#include <QtNetwork/QNetworkReply>
#include <QtNetwork/QSslError>
#include <QtSql/QSqlDatabase>

#include <QtContacts/QContactManager>
#include <QtContacts/QContact>

USE_CONTACTS_NAMESPACE

class FacebookContactSyncAdaptor : public FacebookDataTypeSyncAdaptor
{
Q_OBJECT

public:
FacebookContactSyncAdaptor(SyncService *syncService, QObject *parent);
~FacebookContactSyncAdaptor();

void sync(const QString &dataType);

protected: // implementing FacebookDataTypeSyncAdaptor interface
void purgeDataForOldAccounts(const QList<int> &oldIds);
void beginSync(int accountId, const QString &accessToken);

private:
void requestData(int accountId, const QString &accessToken,
const QString &fbFriendId = QString(),
const QString &avatarUrl = QString(),
const QString &avatarType = QString(),
const QString &continuationRequest = QString(),
const QDateTime &syncTimestamp = QDateTime());
void purgeAccount(int pid);

private Q_SLOTS:
void friendsFinishedHandler();
void avatarFinishedHandler();

private:
QSqlDatabase m_contactSyncDb;
QContactManager *m_contactManager;

// for server-side removal detection.
QMultiMap<int, QString> m_cachedFriendIds; // local friends per account
QMultiMap<int, QString> m_serverFriendIds; // server-side friends per account
void initRemovalDetectionLists();
void clearRemovalDetectionLists(); // to avoid spurious removal of cached data if error occurs.
void purgeDetectedRemovals();
bool purgeFriend(const QString &friendId, int accountId, bool purgeContact);
QList<QContactId> contactIdsForGuid(const QString &fbuid);
QContact newOrExistingContact(const QString &fbuid, bool *isNewContact);
bool avatarUrlIsDifferent(const QString &avatarType, const QString &fbFriendId, int accountId, const QString &avatarUrl);
bool removeAvatarFromDisk(const QString &fbFriendId, int accountId, const QString &avatarType);
void saveImageAndUpdateDatabase(int accountId, const QString &avatarType, const QString &fbFriendId, const QString &avatarUrl, const QByteArray &data);
void parseContactDetails(const QVariantMap &blobDetails, int accountId);
void requestAvatars(const QString &accessToken);
void saveAvatars();
void saveParsedContacts(int accountId);
QMap<QString, QContact> m_contactsToSave;
QStringList m_newContactsToSave;
struct AvatarRequestData {
int accountId; // we have to store accountId as the cache is processed asynchronously, unlike the m_contactsToSave cache.
QString fbuid;
QString url;
QString type;
};
struct AvatarReplyData {
int accountId;
QString fbuid;
QString url;
QString type;
QByteArray data;
};
QList<AvatarRequestData> m_avatarsToRequest;
QList<AvatarReplyData> m_avatarsToSave;
int m_avatarsSemaphore;

// for busy/inactive detection.
void decrementSemaphore(int accountId);
void incrementSemaphore(int accountId);
QMap<int, int> m_accountSyncSemaphores;
};

#endif // FACEBOOKCONTACTSYNCADAPTOR_H
10 changes: 6 additions & 4 deletions src/facebook/facebookdatatypesyncadaptor.cpp
Expand Up @@ -137,8 +137,9 @@ void FacebookDataTypeSyncAdaptor::errorHandler(QNetworkReply::NetworkError err)
TRACE(SOCIALD_ERROR,
QString(QLatin1String("error: %1 request with account %2 experienced error: %3"))
.arg(SyncService::dataType(m_dataType)).arg(sender()->property("accountId").toInt()).arg(err));
// the error is an incomprehensible enum value, but that doesn't matter to users.
changeStatus(SocialNetworkSyncAdaptor::Error);
// set "isError" on the reply so that adapters know to ignore the result in the finished() handler
sender()->setProperty("isError", QVariant::fromValue<bool>(true));
// Note: not all errors are "unrecoverable" errors, so we don't change the status here.
}

void FacebookDataTypeSyncAdaptor::sslErrorsHandler(const QList<QSslError> &errs)
Expand All @@ -153,8 +154,9 @@ void FacebookDataTypeSyncAdaptor::sslErrorsHandler(const QList<QSslError> &errs)
TRACE(SOCIALD_ERROR,
QString(QLatin1String("error: %1 request with account %2 experienced ssl errors: %3"))
.arg(SyncService::dataType(m_dataType)).arg(sender()->property("accountId").toInt()).arg(sslerrs));

changeStatus(SocialNetworkSyncAdaptor::Error);
// set "isError" on the reply so that adapters know to ignore the result in the finished() handler
sender()->setProperty("isError", QVariant::fromValue<bool>(true));
// Note: not all errors are "unrecoverable" errors, so we don't change the status here.
}

QVariantMap FacebookDataTypeSyncAdaptor::parseReplyData(const QByteArray &replyData, bool *ok)
Expand Down
4 changes: 4 additions & 0 deletions src/socialnetworksyncadaptor.cpp
Expand Up @@ -74,11 +74,15 @@ void SocialNetworkSyncAdaptor::checkAccounts(SyncService::DataType dataType, QLi
TRACE(SOCIALD_DEBUG,
QString(QLatin1String("have found %1 accounts which support a sync service; determining old/new/update sets..."))
.arg(currentIds.size()));

for (int i = 0; i < currentIds.size(); ++i) {
int currId = currentIds.at(i);
Account *act = m_accountManager->account(currId);
if (!act || !(act->supportedServiceNames().size() > 0 &&
act->supportedServiceNames().at(0).startsWith(m_serviceName))) {
TRACE(SOCIALD_DEBUG,
QString(QLatin1String("account %1 does not support service %2, ignoring"))
.arg(currId).arg(m_serviceName));
continue; // not same account as m_serviceName. Ignore it.
}

Expand Down
6 changes: 5 additions & 1 deletion src/syncservice.cpp
Expand Up @@ -11,6 +11,7 @@

#include "twitter/twitterhometimelinesyncadaptor.h"
#include "twitter/twittermentiontimelinesyncadaptor.h"
#include "facebook/facebookcontactsyncadaptor.h"
#include "facebook/facebookimagesyncadaptor.h"
#include "facebook/facebooknotificationsyncadaptor.h"
#include "facebook/facebookpostsyncadaptor.h"
Expand Down Expand Up @@ -82,7 +83,8 @@ SyncServicePrivate::SyncServicePrivate(const QString &connectionName, SyncServic
m_supportedDataTypes.insert(facebookService, QStringList() <<
SyncService::dataType(SyncService::Notifications) <<
SyncService::dataType(SyncService::Images) <<
SyncService::dataType(SyncService::Posts));
SyncService::dataType(SyncService::Posts) <<
SyncService::dataType(SyncService::Contacts));
// TODO: Contacts, Calendar Events
}

Expand Down Expand Up @@ -125,6 +127,8 @@ SocialNetworkSyncAdaptor *SyncServicePrivate::createAdaptor(const QString &socia
return new FacebookImageSyncAdaptor(q, parent);
} else if (dataType == SyncService::dataType(SyncService::Posts)) {
return new FacebookPostSyncAdaptor(q, parent);
} else if (dataType == SyncService::dataType(SyncService::Contacts)) {
return new FacebookContactSyncAdaptor(q, parent);
} else {
return 0;
}
Expand Down
20 changes: 20 additions & 0 deletions src/xml/sync/facebook.Contacts.xml
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<profile name="facebook.Contacts" type="sync" >
<!--This key is used for display name of the profile in UI -->
<key name="displayname" value="Facebook Contacts"/>
<!--This key is used to tell sync-fw if the profile is enabled or not -->
<key name="enabled" value="true" />
<!--This key is used if accounts fw is used -->
<key name="use_accounts" value="false" />
<!--This key is used if profile needs to be hidden in UI -->
<key name="hidden" value="false" />
<!--This key is used to tell the sync-fw which service to use -->
<profile name="sociald" type="client" >
<key name="Sync Direction" value="from-remote" />
</profile>

<!--This is used by sync-fw to set customized scheduling information for the profile -->
<schedule enabled="false" interval="1" days="1,2,3,4,5,6,7" syncconfiguredtime="" time="">
</schedule>
<key name="category" value="facebook.Contacts" />
</profile>

0 comments on commit 4fdca83

Please sign in to comment.