2 Situare - A location system for Facebook
3 Copyright (C) 2010 Ixonos Plc. Authors:
5 Henri Lampela - henri.lampela@ixonos.com
7 Situare is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 version 2 as published by the Free Software Foundation.
11 Situare is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with Situare; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
22 #include <qjson/parser.h>
24 #include <QtAlgorithms>
26 #include <QNetworkReply>
28 #include <QStringList>
32 #include "network/networkaccessmanager.h"
33 #include "situarecommon.h"
34 #include "ui/avatarimage.h"
36 #include "situareservice.h"
38 SituareService::SituareService(QObject *parent)
42 qDebug() << __PRETTY_FUNCTION__;
44 m_networkManager = new NetworkAccessManager(this);
45 connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
46 this, SLOT(requestFinished(QNetworkReply*)), Qt::QueuedConnection);
48 m_imageFetcher = new ImageFetcher(new NetworkAccessManager(this), this);
49 connect(this, SIGNAL(fetchImage(QUrl)),
50 m_imageFetcher, SLOT(fetchImage(QUrl)));
51 connect(m_imageFetcher, SIGNAL(imageReceived(QUrl,QPixmap)),
52 this, SLOT(imageReceived(QUrl, QPixmap)));
53 connect(m_imageFetcher, SIGNAL(error(int, int)),
54 this, SIGNAL(error(int, int)));
57 SituareService::~SituareService()
59 qDebug() << __PRETTY_FUNCTION__;
66 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
67 m_friendsList.clear();
70 void SituareService::fetchLocations()
72 qDebug() << __PRETTY_FUNCTION__;
74 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
75 m_credentials.sessionKey(), m_credentials.sessionSecret(),
76 m_credentials.sig(), EN_LOCALE);
78 QUrl url = formUrl(SITUARE_URL, GET_LOCATIONS);
79 sendRequest(url, COOKIE, cookie);
82 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
84 qDebug() << __PRETTY_FUNCTION__;
86 QString cookie = formCookie(API_KEY, m_credentials.expires(),m_credentials.userID(),
87 m_credentials.sessionKey(), m_credentials.sessionSecret(),
88 m_credentials.sig(), EN_LOCALE);
90 QString urlParameters = formUrlParameters(coordinates);
91 urlParameters.append(JSON_FORMAT);
92 QUrl url = formUrl(SITUARE_URL, REVERSE_GEO, urlParameters);
94 sendRequest(url, COOKIE, cookie);
97 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
100 qDebug() << __PRETTY_FUNCTION__;
102 QString urlParameters = formUrlParameters(coordinates, status, publish);
103 QUrl url = formUrl(SITUARE_URL, UPDATE_LOCATION, urlParameters);
105 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
106 m_credentials.sessionKey(), m_credentials.sessionSecret(),
107 m_credentials.sig(), EN_LOCALE);
109 sendRequest(url, COOKIE, cookie);
112 QString SituareService::formCookie(const QString &apiKeyValue, QString expiresValue,
113 QString userValue, QString sessionKeyValue,
114 QString sessionSecretValue, const QString &signatureValue,
115 const QString &localeValue)
117 qDebug() << __PRETTY_FUNCTION__;
124 QString sessionSecret;
127 QString signature = EQUAL_MARK;
128 QStringList variableList;
130 signature.append(signatureValue);
131 apiKey.append(apiKeyValue);
132 apiKey.append(UNDERLINE_MARK);
135 user.append(EQUAL_MARK);
136 expires.append(EXPIRES);
137 expires.append(EQUAL_MARK);
138 sessionKey.append(SESSION_KEY);
139 sessionKey.append(EQUAL_MARK);
140 sessionSecret.append(SESSION_SECRET);
141 sessionSecret.append(EQUAL_MARK);
142 locale.append(LOCALE);
143 locale.append(EQUAL_MARK);
144 locale.append(localeValue);
146 variableList.append(expires.append(expiresValue.append(BREAK_MARK)));
147 variableList.append(sessionKey.append(sessionKeyValue.append(BREAK_MARK)));
148 variableList.append(user.append(userValue).append(BREAK_MARK));
149 variableList.append(sessionSecret.append(sessionSecretValue.append(BREAK_MARK)));
151 cookie.append(BREAK_MARK);
153 foreach(variable, variableList) {
154 cookie.append(apiKey);
155 cookie.append(variable);
157 apiKey.remove(UNDERLINE_MARK);
158 cookie.append(apiKey);
159 cookie.append(signature);
160 cookie.append(BREAK_MARK);
161 cookie.append(locale);
168 QUrl SituareService::formUrl(const QString &baseUrl, const QString &phpScript,
169 QString urlParameters)
171 qDebug() << __PRETTY_FUNCTION__;
174 urlString.append(baseUrl);
175 urlString.append(phpScript);
176 if(!urlParameters.isEmpty())
177 urlString.append(urlParameters);
179 QUrl url = QUrl(urlString);
186 QString SituareService::formUrlParameters(const GeoCoordinate &coordinates, QString status,
189 qDebug() << __PRETTY_FUNCTION__;
191 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
192 // additional digit is added for maximum precision
193 const int COORDINATE_PRECISION = 10;
197 parameters.append(QUESTION_MARK);
198 parameters.append(LATITUDE);
199 parameters.append(EQUAL_MARK);
200 parameters.append(QString::number(coordinates.latitude(), 'f', COORDINATE_PRECISION));
201 parameters.append(AMBERSAND_MARK);
202 parameters.append(LONGTITUDE);
203 parameters.append(EQUAL_MARK);
204 parameters.append(QString::number(coordinates.longitude(), 'f', COORDINATE_PRECISION));
206 parameters.append(AMBERSAND_MARK);
207 parameters.append(PUBLISH);
208 parameters.append(EQUAL_MARK);
211 parameters.append(PUBLISH_TRUE);
213 parameters.append(PUBLISH_FALSE);
215 if(!status.isEmpty()) {
216 parameters.append(AMBERSAND_MARK);
217 parameters.append(DATA);
218 parameters.append(EQUAL_MARK);
219 parameters.append(status);
225 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
227 qDebug() << __PRETTY_FUNCTION__;
229 QNetworkRequest request;
232 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
233 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
235 QNetworkReply *reply = m_networkManager->get(request, true);
237 m_currentRequests.append(reply);
240 void SituareService::requestFinished(QNetworkReply *reply)
242 qDebug() << __PRETTY_FUNCTION__;
245 if (m_currentRequests.contains(reply)) {
247 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
249 if (reply->error()) {
250 emit error(ErrorContext::NETWORK, reply->error());
252 QByteArray replyArray = reply->readAll();
253 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
255 if(replyArray == ERROR_LAT.toAscii()) {
256 qDebug() << "Error: " << ERROR_LAT;
257 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
258 } else if(replyArray == ERROR_LON.toAscii()) {
259 qDebug() << "Error: " << ERROR_LON;
260 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
261 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
262 qDebug() << "Error: " << ERROR_SESSION;
263 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
264 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
265 qDebug() << "JSON string";
266 parseUserData(replyArray);
267 } else if(replyArray.isEmpty()) {
268 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
269 emit updateWasSuccessful();
271 // session credentials are invalid
272 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
276 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
279 m_currentRequests.removeAll(reply);
280 reply->deleteLater();
284 void SituareService::credentialsReady(const FacebookCredentials &credentials)
286 qDebug() << __PRETTY_FUNCTION__;
288 m_credentials = credentials;
291 void SituareService::parseUserData(const QByteArray &jsonReply)
293 qDebug() << __PRETTY_FUNCTION__;
295 m_defaultImage = false;
297 QJson::Parser parser;
300 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
302 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
306 if(result.contains("ErrorCode")) {
307 QVariant errorVariant = result.value("ErrorCode");
308 emit error(ErrorContext::SITUARE, errorVariant.toInt());
310 } else if(result.contains("user")) {
312 QVariant userVariant = result.value("user");
313 QMap<QString, QVariant> userMap = userVariant.toMap();
315 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
317 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
319 if(imageUrl.isEmpty()) {
320 // user doesn't have profile image, so we need to get him a silhouette image
321 m_defaultImage = true;
324 QString address = userMap["address"].toString();
325 if(address.isEmpty()) {
326 QStringList location;
327 location.append(QString::number(coordinates.latitude()));
328 location.append(QString::number(coordinates.longitude()));
329 address = location.join(", ");
332 User user = User(address, coordinates, userMap["name"].toString(),
333 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
334 true, userMap["uid"].toString());
336 QList<User> tmpFriendsList;
338 foreach (QVariant friendsVariant, result["friends"].toList()) {
339 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
340 QVariant distance = friendMap["distance"];
341 QMap<QString, QVariant> distanceMap = distance.toMap();
343 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
345 QUrl imageUrl = friendMap["profile_pic"].toUrl();
347 if(imageUrl.isEmpty()) {
348 // friend doesn't have profile image, so we need to get him a silhouette image
349 m_defaultImage = true;
352 QString address = friendMap["address"].toString();
353 if(address.isEmpty()) {
354 QStringList location;
355 location.append(QString::number(coordinates.latitude()));
356 location.append(QString::number(coordinates.longitude()));
357 address = location.join(", ");
360 User buddy = User(address, coordinates, friendMap["name"].toString(),
361 friendMap["note"].toString(), imageUrl,
362 friendMap["timestamp"].toString(),
363 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
364 distanceMap["value"].toDouble());
366 tmpFriendsList.append(buddy);
369 QList<QUrl> imageUrlList; // url list for images
371 // set unchanged profile images or add new images to imageUrlList for downloading
373 if(m_user->profileImageUrl() != user.profileImageUrl()) {
374 if(!user.profileImageUrl().isEmpty())
375 imageUrlList.append(user.profileImageUrl());
377 user.setProfileImage(m_user->profileImage());
380 if(!user.profileImageUrl().isEmpty())
381 imageUrlList.append(user.profileImageUrl());
384 // clear old user object
390 // create new user object from temporary user object
391 m_user = new User(user);
393 // set unchanged profile images or add new images to imageUrlList for downloading
394 if(!m_friendsList.isEmpty()) {
395 foreach(User tmpBuddy, tmpFriendsList) {
396 if(!tmpBuddy.profileImageUrl().isEmpty()) {
398 foreach(User *buddy, m_friendsList) {
399 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
400 tmpBuddy.setProfileImage(buddy->profileImage());
405 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
406 imageUrlList.append(tmpBuddy.profileImageUrl());
410 foreach(User buddy, tmpFriendsList) {
411 if(!buddy.profileImageUrl().isEmpty())
412 imageUrlList.append(buddy.profileImageUrl());
416 // clear old friendlist
417 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
418 m_friendsList.clear();
420 // populate new friendlist with temporary friendlist's data
421 foreach(User tmpFriendItem, tmpFriendsList) {
422 User *friendItem = new User(tmpFriendItem);
423 m_friendsList.append(friendItem);
425 tmpFriendsList.clear();
427 emit userDataChanged(m_user, m_friendsList);
429 // set silhouette image to imageUrlList for downloading
431 imageUrlList.append(QUrl(SILHOUETTE_URL));
433 addProfileImages(imageUrlList);
434 imageUrlList.clear();
436 QVariant address = result.value("address");
437 if(!address.toString().isEmpty()) {
438 emit reverseGeoReady(address.toString());
440 QStringList coordinates;
441 coordinates.append(result.value("lat").toString());
442 coordinates.append(result.value("lon").toString());
444 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
445 emit reverseGeoReady(coordinates.join(", "));
451 void SituareService::imageReceived(const QUrl &url, const QPixmap &image)
453 qDebug() << __PRETTY_FUNCTION__;
454 qDebug() << "Image URL: " << url << " size :" << image.size();
456 // assign facebook silhouette image to all who doesn't have a profile image
457 if(url == QUrl(SILHOUETTE_URL)) {
458 if(m_user->profileImageUrl().isEmpty()) {
459 m_user->setProfileImage(AvatarImage::create(image, AvatarImage::Large));
460 emit imageReady(m_user);
462 foreach(User *friendItem, m_friendsList) {
463 if(friendItem->profileImageUrl().isEmpty()) {
464 friendItem->setProfileImage(AvatarImage::create(image, AvatarImage::Small));
465 emit imageReady(friendItem);
470 if (m_user->profileImageUrl() == url) {
471 m_user->setProfileImage(AvatarImage::create(image, AvatarImage::Large));
472 emit imageReady(m_user);
475 foreach(User *friendItem, m_friendsList) {
476 if(friendItem->profileImageUrl() == url) {
477 friendItem->setProfileImage(AvatarImage::create(image, AvatarImage::Small));
478 emit imageReady(friendItem);
483 void SituareService::addProfileImages(const QList<QUrl> &imageUrlList)
485 qDebug() << __PRETTY_FUNCTION__;
487 foreach(QUrl url, imageUrlList) {
488 emit fetchImage(url);
492 void SituareService::clearUserData()
494 qDebug() << __PRETTY_FUNCTION__;
496 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
497 m_friendsList.clear();
503 emit userDataChanged(m_user, m_friendsList);