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,
24 #include <QtAlgorithms>
26 #include <QtNetwork/QNetworkReply>
28 #include <QStringList>
33 #include "network/networkaccessmanager.h"
34 #include "situarecommon.h"
35 #include "ui/avatarimage.h"
37 #include "situareservice.h"
39 SituareService::SituareService(NetworkAccessManager *networkManager, ImageFetcher *imageFetcher,
44 qDebug() << __PRETTY_FUNCTION__;
46 m_networkManager = networkManager;
47 connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
48 this, SLOT(requestFinished(QNetworkReply*)), Qt::QueuedConnection);
50 m_imageFetcher = imageFetcher;
51 connect(this, SIGNAL(fetchImage(QString, QUrl)),
52 m_imageFetcher, SLOT(fetchImage(QString, QUrl)));
53 connect(m_imageFetcher, SIGNAL(imageReceived(QString,QPixmap)),
54 this, SLOT(imageReceived(QString,QPixmap)));
55 connect(m_imageFetcher, SIGNAL(error(int, int)),
56 this, SIGNAL(error(int, int)));
58 m_database = new Database(this);
59 m_database->openDatabase();
60 m_database->createNotificationTable();
61 m_database->createUserTable();
62 m_database->createTagTable();
63 m_database->createUserTagTable();
66 SituareService::~SituareService()
68 qDebug() << __PRETTY_FUNCTION__;
75 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
76 m_friendsList.clear();
79 void SituareService::fetchNotifications()
81 qDebug() << __PRETTY_FUNCTION__;
83 QByteArray arr = m_database->getNotifications(m_user->userId().toULongLong());
85 parseNotificationsData(arr);
88 void SituareService::fetchPeopleWithSimilarInterest(const GeoCoordinate &southWestCoordinates,
89 const GeoCoordinate &northEastCoordinates)
91 qDebug() << __PRETTY_FUNCTION__;
93 QByteArray arr = m_database->getInterestingPeople(m_user->userId().toULongLong(),
95 northEastCoordinates);
97 parseInterestingPeopleData(arr);
100 void SituareService::fetchLocations()
102 qDebug() << __PRETTY_FUNCTION__;
104 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
105 m_credentials.sessionKey(), m_credentials.sessionSecret(),
106 m_credentials.sig(), EN_LOCALE);
108 QUrl url = formUrl(SITUARE_URL, GET_LOCATIONS);
109 sendRequest(url, COOKIE, cookie);
112 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
114 qDebug() << __PRETTY_FUNCTION__;
116 QString cookie = formCookie(API_KEY, m_credentials.expires(),m_credentials.userID(),
117 m_credentials.sessionKey(), m_credentials.sessionSecret(),
118 m_credentials.sig(), EN_LOCALE);
120 QString urlParameters = formUrlParameters(coordinates);
121 urlParameters.append(JSON_FORMAT);
122 QUrl url = formUrl(SITUARE_URL, REVERSE_GEO, urlParameters);
124 sendRequest(url, COOKIE, cookie);
127 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
130 qDebug() << __PRETTY_FUNCTION__;
132 QString urlParameters = formUrlParameters(coordinates, status, publish);
133 QUrl url = formUrl(SITUARE_URL, UPDATE_LOCATION, urlParameters);
135 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
136 m_credentials.sessionKey(), m_credentials.sessionSecret(),
137 m_credentials.sig(), EN_LOCALE);
139 sendRequest(url, COOKIE, cookie);
142 QString SituareService::formCookie(const QString &apiKeyValue, QString expiresValue,
143 QString userValue, QString sessionKeyValue,
144 QString sessionSecretValue, const QString &signatureValue,
145 const QString &localeValue)
147 qDebug() << __PRETTY_FUNCTION__;
154 QString sessionSecret;
157 QString signature = EQUAL_MARK;
158 QStringList variableList;
160 signature.append(signatureValue);
161 apiKey.append(apiKeyValue);
162 apiKey.append(UNDERLINE_MARK);
165 user.append(EQUAL_MARK);
166 expires.append(EXPIRES);
167 expires.append(EQUAL_MARK);
168 sessionKey.append(SESSION_KEY);
169 sessionKey.append(EQUAL_MARK);
170 sessionSecret.append(SESSION_SECRET);
171 sessionSecret.append(EQUAL_MARK);
172 locale.append(LOCALE);
173 locale.append(EQUAL_MARK);
174 locale.append(localeValue);
176 variableList.append(expires.append(expiresValue.append(BREAK_MARK)));
177 variableList.append(sessionKey.append(sessionKeyValue.append(BREAK_MARK)));
178 variableList.append(user.append(userValue).append(BREAK_MARK));
179 variableList.append(sessionSecret.append(sessionSecretValue.append(BREAK_MARK)));
181 cookie.append(BREAK_MARK);
183 foreach(variable, variableList) {
184 cookie.append(apiKey);
185 cookie.append(variable);
187 apiKey.remove(UNDERLINE_MARK);
188 cookie.append(apiKey);
189 cookie.append(signature);
190 cookie.append(BREAK_MARK);
191 cookie.append(locale);
198 QUrl SituareService::formUrl(const QString &baseUrl, const QString &phpScript,
199 QString urlParameters)
201 qDebug() << __PRETTY_FUNCTION__;
204 urlString.append(baseUrl);
205 urlString.append(phpScript);
206 if(!urlParameters.isEmpty())
207 urlString.append(urlParameters);
209 QUrl url = QUrl(urlString);
216 QString SituareService::formUrlParameters(const GeoCoordinate &coordinates, QString status,
219 qDebug() << __PRETTY_FUNCTION__;
221 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
222 // additional digit is added for maximum precision
223 const int COORDINATE_PRECISION = 10;
227 parameters.append(QUESTION_MARK);
228 parameters.append(LATITUDE);
229 parameters.append(EQUAL_MARK);
230 parameters.append(QString::number(coordinates.latitude(), 'f', COORDINATE_PRECISION));
231 parameters.append(AMBERSAND_MARK);
232 parameters.append(LONGTITUDE);
233 parameters.append(EQUAL_MARK);
234 parameters.append(QString::number(coordinates.longitude(), 'f', COORDINATE_PRECISION));
236 parameters.append(AMBERSAND_MARK);
237 parameters.append(PUBLISH);
238 parameters.append(EQUAL_MARK);
241 parameters.append(PUBLISH_TRUE);
243 parameters.append(PUBLISH_FALSE);
245 if(!status.isEmpty()) {
246 parameters.append(AMBERSAND_MARK);
247 parameters.append(DATA);
248 parameters.append(EQUAL_MARK);
249 parameters.append(status);
255 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
257 qDebug() << __PRETTY_FUNCTION__;
259 QNetworkRequest request;
262 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
263 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
265 QNetworkReply *reply = m_networkManager->get(request, true);
267 m_currentRequests.append(reply);
270 void SituareService::requestFinished(QNetworkReply *reply)
272 qDebug() << __PRETTY_FUNCTION__;
275 if (m_currentRequests.contains(reply)) {
277 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
279 if (reply->error()) {
280 emit error(ErrorContext::NETWORK, reply->error());
282 QByteArray replyArray = reply->readAll();
283 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
285 if(replyArray == ERROR_LAT.toAscii()) {
286 qDebug() << "Error: " << ERROR_LAT;
287 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
288 } else if(replyArray == ERROR_LON.toAscii()) {
289 qDebug() << "Error: " << ERROR_LON;
290 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
291 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
292 qDebug() << "Error: " << ERROR_SESSION;
293 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
294 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
295 qDebug() << "JSON string";
296 parseUserData(replyArray);
297 } else if(replyArray.isEmpty()) {
298 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
299 emit updateWasSuccessful();
301 // session credentials are invalid
302 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
306 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
309 m_currentRequests.removeAll(reply);
310 reply->deleteLater();
314 void SituareService::credentialsReady(const FacebookCredentials &credentials)
316 qDebug() << __PRETTY_FUNCTION__;
318 m_credentials = credentials;
321 void SituareService::parseInterestingPeopleData(const QByteArray &jsonReply)
323 qDebug() << __PRETTY_FUNCTION__;
325 QJson::Parser parser;
328 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
331 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
334 QList<User> interestingPeople;
336 foreach (QVariant personVariant, result["people"].toList()) {
338 QMap<QString, QVariant> person = personVariant.toMap();
339 user.setUserId(person["uid"].toString());
340 user.setName(person["name"].toString());
341 user.setProfileImage(AvatarImage::create(
342 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
343 user.setProfileImageUrl(person["image_url"].toUrl());
345 interestingPeople.append(user);
347 emit fetchImage(user.userId(), user.profileImageUrl());
350 emit interestingPeopleReceived(interestingPeople);
354 void SituareService::parseNotificationsData(const QByteArray &jsonReply)
356 QJson::Parser parser;
359 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
362 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
365 QList<Notification> notifications;
367 foreach (QVariant notificationVariant, result["notifications"].toList()) {
368 Notification notification;
369 QMap<QString, QVariant> notificationMap = notificationVariant.toMap();
370 notification.setId(notificationMap["id"].toString());
371 notification.setSenderId(notificationMap["sender_id"].toString());
372 notification.setSenderName(notificationMap["sender_name"].toString());
373 uint timestampSeconds = notificationMap["timestamp"].toUInt();
374 notification.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
375 notification.setText(notificationMap["text"].toString());
376 notification.setImage(AvatarImage::create(
377 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
379 notifications.append(notification);
381 emit fetchImage(notification.id(), notificationMap["image_url"].toString());
384 emit notificationsReceived(notifications);
388 void SituareService::parseUserData(const QByteArray &jsonReply)
390 qDebug() << __PRETTY_FUNCTION__;
392 m_defaultImage = false;
394 QJson::Parser parser;
397 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
400 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
404 if(result.contains("ErrorCode")) {
405 QVariant errorVariant = result.value("ErrorCode");
406 emit error(ErrorContext::SITUARE, errorVariant.toInt());
408 } else if(result.contains("user")) {
410 QVariant userVariant = result.value("user");
411 QMap<QString, QVariant> userMap = userVariant.toMap();
413 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
415 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
417 QString address = userMap["address"].toString();
418 if(address.isEmpty()) {
419 QStringList location;
420 location.append(QString::number(coordinates.latitude()));
421 location.append(QString::number(coordinates.longitude()));
422 address = location.join(", ");
425 User user = User(address, coordinates, userMap["name"].toString(),
426 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
427 true, userMap["uid"].toString());
429 if(imageUrl.isEmpty()) {
430 // user doesn't have profile image, so we need to get him a silhouette image
431 m_defaultImage = true;
432 user.setProfileImage(AvatarImage::create(
433 QPixmap(":/res/images/empty_avatar_big.png"), AvatarImage::Large));
436 QList<User> tmpFriendsList;
438 foreach (QVariant friendsVariant, result["friends"].toList()) {
439 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
440 QVariant distance = friendMap["distance"];
441 QMap<QString, QVariant> distanceMap = distance.toMap();
443 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
445 QUrl imageUrl = friendMap["profile_pic"].toUrl();
447 QString address = friendMap["address"].toString();
448 if(address.isEmpty()) {
449 QStringList location;
450 location.append(QString::number(coordinates.latitude()));
451 location.append(QString::number(coordinates.longitude()));
452 address = location.join(", ");
455 User buddy = User(address, coordinates, friendMap["name"].toString(),
456 friendMap["note"].toString(), imageUrl,
457 friendMap["timestamp"].toString(),
458 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
459 distanceMap["value"].toDouble());
461 if(imageUrl.isEmpty()) {
462 // friend doesn't have profile image, so we need to get him a silhouette image
463 m_defaultImage = true;
464 buddy.setProfileImage(AvatarImage::create(
465 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
468 tmpFriendsList.append(buddy);
471 QHash<QString, QUrl> imageUrlList; // url list for images
473 // set unchanged profile images or add new images to imageUrlList for downloading
475 if(m_user->profileImageUrl() != user.profileImageUrl()) {
476 if(!user.profileImageUrl().isEmpty())
477 imageUrlList.insert(user.userId(), user.profileImageUrl());
479 user.setProfileImage(m_user->profileImage());
482 if(!user.profileImageUrl().isEmpty())
483 imageUrlList.insert(user.userId(), user.profileImageUrl());
486 // clear old user object
492 // create new user object from temporary user object
493 m_user = new User(user);
495 // set unchanged profile images or add new images to imageUrlList for downloading
496 if(!m_friendsList.isEmpty()) {
497 foreach(User tmpBuddy, tmpFriendsList) {
498 if(!tmpBuddy.profileImageUrl().isEmpty()) {
500 foreach(User *buddy, m_friendsList) {
501 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
502 tmpBuddy.setProfileImage(buddy->profileImage());
507 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
508 imageUrlList.insert(tmpBuddy.userId(), tmpBuddy.profileImageUrl());
512 foreach(User buddy, tmpFriendsList) {
513 if(!buddy.profileImageUrl().isEmpty())
514 imageUrlList.insert(buddy.userId(), buddy.profileImageUrl());
518 // clear old friendlist
519 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
520 m_friendsList.clear();
522 // populate new friendlist with temporary friendlist's data
523 foreach(User tmpFriendItem, tmpFriendsList) {
524 User *friendItem = new User(tmpFriendItem);
525 m_friendsList.append(friendItem);
527 tmpFriendsList.clear();
529 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
530 m_user->setTags(getTags(m_user->userId()));
532 emit userDataChanged(m_user, m_friendsList);
534 // set silhouette image to imageUrlList for downloading
536 imageUrlList.insert("", QUrl(SILHOUETTE_URL));
538 addProfileImages(imageUrlList);
539 imageUrlList.clear();
542 QVariant address = result.value("address");
543 if(!address.toString().isEmpty()) {
544 emit reverseGeoReady(address.toString());
546 QStringList coordinates;
547 coordinates.append(result.value("lat").toString());
548 coordinates.append(result.value("lon").toString());
550 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
551 emit reverseGeoReady(coordinates.join(", "));
557 void SituareService::imageReceived(const QString &id, const QPixmap &image)
559 qDebug() << __PRETTY_FUNCTION__;
561 if (m_user->userId() == id)
562 emit imageReady(id, AvatarImage::create(image, AvatarImage::Large));
564 emit imageReady(id, AvatarImage::create(image, AvatarImage::Small));
567 void SituareService::addProfileImages(const QHash<QString, QUrl> &imageUrlList)
569 qDebug() << __PRETTY_FUNCTION__;
571 QHashIterator<QString, QUrl> imageUrlListIterator(imageUrlList);
573 while (imageUrlListIterator.hasNext()) {
574 imageUrlListIterator.next();
575 emit fetchImage(imageUrlListIterator.key(), imageUrlListIterator.value());
579 void SituareService::clearUserData()
581 qDebug() << __PRETTY_FUNCTION__;
583 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
584 m_friendsList.clear();
590 emit userDataChanged(m_user, m_friendsList);
593 QStringList SituareService::getTags(const QString &userId)
595 qDebug() << __PRETTY_FUNCTION__;
597 return m_database->getTags(userId.toInt());
600 void SituareService::updateTags(const QString &userId, const QStringList &tags)
602 qDebug() << __PRETTY_FUNCTION__;
604 foreach (QString tag, tags)
605 m_database->addTag(userId.toInt(), tag);