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 <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(QObject *parent)
43 qDebug() << __PRETTY_FUNCTION__;
45 m_networkManager = new NetworkAccessManager(this);
46 connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
47 this, SLOT(requestFinished(QNetworkReply*)), Qt::QueuedConnection);
49 m_imageFetcher = new ImageFetcher(new NetworkAccessManager(this), this);
50 connect(this, SIGNAL(fetchImage(QString, QUrl)),
51 m_imageFetcher, SLOT(fetchImage(QString, QUrl)));
52 connect(m_imageFetcher, SIGNAL(imageReceived(QString,QPixmap)),
53 this, SLOT(imageReceived(QString,QPixmap)));
54 connect(m_imageFetcher, SIGNAL(error(int, int)),
55 this, SIGNAL(error(int, int)));
57 m_database = new Database(this);
58 m_database->openDatabase();
59 m_database->createNotificationTable();
60 m_database->createUserTable();
61 m_database->createTagTable();
62 m_database->createUserTagTable();
65 SituareService::~SituareService()
67 qDebug() << __PRETTY_FUNCTION__;
74 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
75 m_friendsList.clear();
78 void SituareService::fetchNotifications()
80 qDebug() << __PRETTY_FUNCTION__;
82 QByteArray arr = m_database->getNotifications(m_user->userId().toULongLong());
84 parseNotificationsData(arr);
87 void SituareService::fetchPeopleWithSimilarInterest(const GeoCoordinate &southWestCoordinates,
88 const GeoCoordinate &northEastCoordinates)
90 qDebug() << __PRETTY_FUNCTION__;
92 QByteArray arr = m_database->getInterestingPeople(m_user->userId().toULongLong(),
94 northEastCoordinates);
96 parseInterestingPeopleData(arr);
99 void SituareService::fetchLocations()
101 qDebug() << __PRETTY_FUNCTION__;
103 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
104 m_credentials.sessionKey(), m_credentials.sessionSecret(),
105 m_credentials.sig(), EN_LOCALE);
107 QUrl url = formUrl(SITUARE_URL, GET_LOCATIONS);
108 sendRequest(url, COOKIE, cookie);
111 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
113 qDebug() << __PRETTY_FUNCTION__;
115 QString cookie = formCookie(API_KEY, m_credentials.expires(),m_credentials.userID(),
116 m_credentials.sessionKey(), m_credentials.sessionSecret(),
117 m_credentials.sig(), EN_LOCALE);
119 QString urlParameters = formUrlParameters(coordinates);
120 urlParameters.append(JSON_FORMAT);
121 QUrl url = formUrl(SITUARE_URL, REVERSE_GEO, urlParameters);
123 sendRequest(url, COOKIE, cookie);
126 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
129 qDebug() << __PRETTY_FUNCTION__;
131 QString urlParameters = formUrlParameters(coordinates, status, publish);
132 QUrl url = formUrl(SITUARE_URL, UPDATE_LOCATION, urlParameters);
134 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
135 m_credentials.sessionKey(), m_credentials.sessionSecret(),
136 m_credentials.sig(), EN_LOCALE);
138 sendRequest(url, COOKIE, cookie);
141 QString SituareService::formCookie(const QString &apiKeyValue, QString expiresValue,
142 QString userValue, QString sessionKeyValue,
143 QString sessionSecretValue, const QString &signatureValue,
144 const QString &localeValue)
146 qDebug() << __PRETTY_FUNCTION__;
153 QString sessionSecret;
156 QString signature = EQUAL_MARK;
157 QStringList variableList;
159 signature.append(signatureValue);
160 apiKey.append(apiKeyValue);
161 apiKey.append(UNDERLINE_MARK);
164 user.append(EQUAL_MARK);
165 expires.append(EXPIRES);
166 expires.append(EQUAL_MARK);
167 sessionKey.append(SESSION_KEY);
168 sessionKey.append(EQUAL_MARK);
169 sessionSecret.append(SESSION_SECRET);
170 sessionSecret.append(EQUAL_MARK);
171 locale.append(LOCALE);
172 locale.append(EQUAL_MARK);
173 locale.append(localeValue);
175 variableList.append(expires.append(expiresValue.append(BREAK_MARK)));
176 variableList.append(sessionKey.append(sessionKeyValue.append(BREAK_MARK)));
177 variableList.append(user.append(userValue).append(BREAK_MARK));
178 variableList.append(sessionSecret.append(sessionSecretValue.append(BREAK_MARK)));
180 cookie.append(BREAK_MARK);
182 foreach(variable, variableList) {
183 cookie.append(apiKey);
184 cookie.append(variable);
186 apiKey.remove(UNDERLINE_MARK);
187 cookie.append(apiKey);
188 cookie.append(signature);
189 cookie.append(BREAK_MARK);
190 cookie.append(locale);
197 QUrl SituareService::formUrl(const QString &baseUrl, const QString &phpScript,
198 QString urlParameters)
200 qDebug() << __PRETTY_FUNCTION__;
203 urlString.append(baseUrl);
204 urlString.append(phpScript);
205 if(!urlParameters.isEmpty())
206 urlString.append(urlParameters);
208 QUrl url = QUrl(urlString);
215 QString SituareService::formUrlParameters(const GeoCoordinate &coordinates, QString status,
218 qDebug() << __PRETTY_FUNCTION__;
220 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
221 // additional digit is added for maximum precision
222 const int COORDINATE_PRECISION = 10;
226 parameters.append(QUESTION_MARK);
227 parameters.append(LATITUDE);
228 parameters.append(EQUAL_MARK);
229 parameters.append(QString::number(coordinates.latitude(), 'f', COORDINATE_PRECISION));
230 parameters.append(AMBERSAND_MARK);
231 parameters.append(LONGTITUDE);
232 parameters.append(EQUAL_MARK);
233 parameters.append(QString::number(coordinates.longitude(), 'f', COORDINATE_PRECISION));
235 parameters.append(AMBERSAND_MARK);
236 parameters.append(PUBLISH);
237 parameters.append(EQUAL_MARK);
240 parameters.append(PUBLISH_TRUE);
242 parameters.append(PUBLISH_FALSE);
244 if(!status.isEmpty()) {
245 parameters.append(AMBERSAND_MARK);
246 parameters.append(DATA);
247 parameters.append(EQUAL_MARK);
248 parameters.append(status);
254 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
256 qDebug() << __PRETTY_FUNCTION__;
258 QNetworkRequest request;
261 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
262 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
264 QNetworkReply *reply = m_networkManager->get(request, true);
266 m_currentRequests.append(reply);
269 void SituareService::requestFinished(QNetworkReply *reply)
271 qDebug() << __PRETTY_FUNCTION__;
274 if (m_currentRequests.contains(reply)) {
276 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
278 if (reply->error()) {
279 emit error(ErrorContext::NETWORK, reply->error());
281 QByteArray replyArray = reply->readAll();
282 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
284 if(replyArray == ERROR_LAT.toAscii()) {
285 qDebug() << "Error: " << ERROR_LAT;
286 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
287 } else if(replyArray == ERROR_LON.toAscii()) {
288 qDebug() << "Error: " << ERROR_LON;
289 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
290 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
291 qDebug() << "Error: " << ERROR_SESSION;
292 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
293 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
294 qDebug() << "JSON string";
295 parseUserData(replyArray);
296 } else if(replyArray.isEmpty()) {
297 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
298 emit updateWasSuccessful();
300 // session credentials are invalid
301 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
305 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
308 m_currentRequests.removeAll(reply);
309 reply->deleteLater();
313 void SituareService::credentialsReady(const FacebookCredentials &credentials)
315 qDebug() << __PRETTY_FUNCTION__;
317 m_credentials = credentials;
320 void SituareService::parseInterestingPeopleData(const QByteArray &jsonReply)
322 qDebug() << __PRETTY_FUNCTION__;
324 QJson::Parser parser;
327 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
330 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
333 QList<User> interestingPeople;
335 foreach (QVariant personVariant, result["people"].toList()) {
337 QMap<QString, QVariant> person = personVariant.toMap();
338 user.setUserId(person["uid"].toString());
339 user.setName(person["name"].toString());
340 user.setProfileImage(AvatarImage::create(
341 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
342 user.setProfileImageUrl(person["image_url"].toUrl());
344 interestingPeople.append(user);
346 emit fetchImage(user.userId(), user.profileImageUrl());
349 emit interestingPeopleReceived(interestingPeople);
353 void SituareService::parseNotificationsData(const QByteArray &jsonReply)
355 QJson::Parser parser;
358 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
361 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
364 QList<Notification> notifications;
366 foreach (QVariant notificationVariant, result["notifications"].toList()) {
367 Notification notification;
368 QMap<QString, QVariant> notificationMap = notificationVariant.toMap();
369 notification.setId(notificationMap["id"].toString());
370 notification.setSenderId(notificationMap["sender_id"].toString());
371 notification.setSenderName(notificationMap["sender_name"].toString());
372 uint timestampSeconds = notificationMap["timestamp"].toUInt();
373 notification.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
374 notification.setText(notificationMap["text"].toString());
375 notification.setImage(AvatarImage::create(
376 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
378 notifications.append(notification);
380 emit fetchImage(notification.id(), notificationMap["image_url"].toString());
383 emit notificationsReceived(notifications);
387 void SituareService::parseUserData(const QByteArray &jsonReply)
389 qDebug() << __PRETTY_FUNCTION__;
391 m_defaultImage = false;
393 QJson::Parser parser;
396 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
399 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
403 if(result.contains("ErrorCode")) {
404 QVariant errorVariant = result.value("ErrorCode");
405 emit error(ErrorContext::SITUARE, errorVariant.toInt());
407 } else if(result.contains("user")) {
409 QVariant userVariant = result.value("user");
410 QMap<QString, QVariant> userMap = userVariant.toMap();
412 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
414 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
416 QString address = userMap["address"].toString();
417 if(address.isEmpty()) {
418 QStringList location;
419 location.append(QString::number(coordinates.latitude()));
420 location.append(QString::number(coordinates.longitude()));
421 address = location.join(", ");
424 User user = User(address, coordinates, userMap["name"].toString(),
425 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
426 true, userMap["uid"].toString());
428 if(imageUrl.isEmpty()) {
429 // user doesn't have profile image, so we need to get him a silhouette image
430 m_defaultImage = true;
431 user.setProfileImage(AvatarImage::create(
432 QPixmap(":/res/images/empty_avatar_big.png"), AvatarImage::Large));
435 QList<User> tmpFriendsList;
437 foreach (QVariant friendsVariant, result["friends"].toList()) {
438 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
439 QVariant distance = friendMap["distance"];
440 QMap<QString, QVariant> distanceMap = distance.toMap();
442 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
444 QUrl imageUrl = friendMap["profile_pic"].toUrl();
446 QString address = friendMap["address"].toString();
447 if(address.isEmpty()) {
448 QStringList location;
449 location.append(QString::number(coordinates.latitude()));
450 location.append(QString::number(coordinates.longitude()));
451 address = location.join(", ");
454 User buddy = User(address, coordinates, friendMap["name"].toString(),
455 friendMap["note"].toString(), imageUrl,
456 friendMap["timestamp"].toString(),
457 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
458 distanceMap["value"].toDouble());
460 if(imageUrl.isEmpty()) {
461 // friend doesn't have profile image, so we need to get him a silhouette image
462 m_defaultImage = true;
463 buddy.setProfileImage(AvatarImage::create(
464 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
467 tmpFriendsList.append(buddy);
470 QHash<QString, QUrl> imageUrlList; // url list for images
472 // set unchanged profile images or add new images to imageUrlList for downloading
474 if(m_user->profileImageUrl() != user.profileImageUrl()) {
475 if(!user.profileImageUrl().isEmpty())
476 imageUrlList.insert(user.userId(), user.profileImageUrl());
478 user.setProfileImage(m_user->profileImage());
481 if(!user.profileImageUrl().isEmpty())
482 imageUrlList.insert(user.userId(), user.profileImageUrl());
485 // clear old user object
491 // create new user object from temporary user object
492 m_user = new User(user);
494 // set unchanged profile images or add new images to imageUrlList for downloading
495 if(!m_friendsList.isEmpty()) {
496 foreach(User tmpBuddy, tmpFriendsList) {
497 if(!tmpBuddy.profileImageUrl().isEmpty()) {
499 foreach(User *buddy, m_friendsList) {
500 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
501 tmpBuddy.setProfileImage(buddy->profileImage());
506 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
507 imageUrlList.insert(tmpBuddy.userId(), tmpBuddy.profileImageUrl());
511 foreach(User buddy, tmpFriendsList) {
512 if(!buddy.profileImageUrl().isEmpty())
513 imageUrlList.insert(buddy.userId(), buddy.profileImageUrl());
517 // clear old friendlist
518 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
519 m_friendsList.clear();
521 // populate new friendlist with temporary friendlist's data
522 foreach(User tmpFriendItem, tmpFriendsList) {
523 User *friendItem = new User(tmpFriendItem);
524 m_friendsList.append(friendItem);
526 tmpFriendsList.clear();
528 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
529 m_user->setTags(getTags(m_user->userId()));
531 emit userDataChanged(m_user, m_friendsList);
533 // set silhouette image to imageUrlList for downloading
535 imageUrlList.insert("", QUrl(SILHOUETTE_URL));
537 addProfileImages(imageUrlList);
538 imageUrlList.clear();
541 QVariant address = result.value("address");
542 if(!address.toString().isEmpty()) {
543 emit reverseGeoReady(address.toString());
545 QStringList coordinates;
546 coordinates.append(result.value("lat").toString());
547 coordinates.append(result.value("lon").toString());
549 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
550 emit reverseGeoReady(coordinates.join(", "));
556 void SituareService::imageReceived(const QString &id, const QPixmap &image)
558 qDebug() << __PRETTY_FUNCTION__;
560 if (m_user->userId() == id)
561 emit imageReady(id, AvatarImage::create(image, AvatarImage::Large));
563 emit imageReady(id, AvatarImage::create(image, AvatarImage::Small));
566 void SituareService::addProfileImages(const QHash<QString, QUrl> &imageUrlList)
568 qDebug() << __PRETTY_FUNCTION__;
570 QHashIterator<QString, QUrl> imageUrlListIterator(imageUrlList);
572 while (imageUrlListIterator.hasNext()) {
573 imageUrlListIterator.next();
574 emit fetchImage(imageUrlListIterator.key(), imageUrlListIterator.value());
578 void SituareService::clearUserData()
580 qDebug() << __PRETTY_FUNCTION__;
582 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
583 m_friendsList.clear();
589 emit userDataChanged(m_user, m_friendsList);
592 QStringList SituareService::getTags(const QString &userId)
594 qDebug() << __PRETTY_FUNCTION__;
596 return m_database->getTags(userId.toInt());
599 void SituareService::updateTags(const QString &userId, const QStringList &tags)
601 qDebug() << __PRETTY_FUNCTION__;
603 foreach (QString tag, tags)
604 m_database->addTag(userId.toInt(), tag);