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::fetchMessages()
81 qDebug() << __PRETTY_FUNCTION__;
83 //Request sent to server does not need the UID
84 QByteArray arr = m_database->getNotifications(613374451);
86 parseMessagesData(arr);
89 void SituareService::fetchPeopleWithSimilarInterest(const GeoCoordinate &southWestCoordinates,
90 const GeoCoordinate &northEastCoordinates)
92 qDebug() << __PRETTY_FUNCTION__;
94 //Request sent to server does not need the UID
95 QByteArray arr = m_database->getInterestingPeople(613374451,
97 northEastCoordinates);
99 parseInterestingPeopleData(arr);
102 void SituareService::fetchLocations()
104 qDebug() << __PRETTY_FUNCTION__;
106 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
107 m_credentials.sessionKey(), m_credentials.sessionSecret(),
108 m_credentials.sig(), EN_LOCALE);
110 QUrl url = formUrl(SITUARE_URL, GET_LOCATIONS);
111 sendRequest(url, COOKIE, cookie);
114 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
116 qDebug() << __PRETTY_FUNCTION__;
118 QString cookie = formCookie(API_KEY, m_credentials.expires(),m_credentials.userID(),
119 m_credentials.sessionKey(), m_credentials.sessionSecret(),
120 m_credentials.sig(), EN_LOCALE);
122 QString urlParameters = formUrlParameters(coordinates);
123 urlParameters.append(JSON_FORMAT);
124 QUrl url = formUrl(SITUARE_URL, REVERSE_GEO, urlParameters);
126 sendRequest(url, COOKIE, cookie);
129 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
132 qDebug() << __PRETTY_FUNCTION__;
134 QString urlParameters = formUrlParameters(coordinates, status, publish);
135 QUrl url = formUrl(SITUARE_URL, UPDATE_LOCATION, urlParameters);
137 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
138 m_credentials.sessionKey(), m_credentials.sessionSecret(),
139 m_credentials.sig(), EN_LOCALE);
141 sendRequest(url, COOKIE, cookie);
144 QString SituareService::formCookie(const QString &apiKeyValue, QString expiresValue,
145 QString userValue, QString sessionKeyValue,
146 QString sessionSecretValue, const QString &signatureValue,
147 const QString &localeValue)
149 qDebug() << __PRETTY_FUNCTION__;
156 QString sessionSecret;
159 QString signature = EQUAL_MARK;
160 QStringList variableList;
162 signature.append(signatureValue);
163 apiKey.append(apiKeyValue);
164 apiKey.append(UNDERLINE_MARK);
167 user.append(EQUAL_MARK);
168 expires.append(EXPIRES);
169 expires.append(EQUAL_MARK);
170 sessionKey.append(SESSION_KEY);
171 sessionKey.append(EQUAL_MARK);
172 sessionSecret.append(SESSION_SECRET);
173 sessionSecret.append(EQUAL_MARK);
174 locale.append(LOCALE);
175 locale.append(EQUAL_MARK);
176 locale.append(localeValue);
178 variableList.append(expires.append(expiresValue.append(BREAK_MARK)));
179 variableList.append(sessionKey.append(sessionKeyValue.append(BREAK_MARK)));
180 variableList.append(user.append(userValue).append(BREAK_MARK));
181 variableList.append(sessionSecret.append(sessionSecretValue.append(BREAK_MARK)));
183 cookie.append(BREAK_MARK);
185 foreach(variable, variableList) {
186 cookie.append(apiKey);
187 cookie.append(variable);
189 apiKey.remove(UNDERLINE_MARK);
190 cookie.append(apiKey);
191 cookie.append(signature);
192 cookie.append(BREAK_MARK);
193 cookie.append(locale);
200 QUrl SituareService::formUrl(const QString &baseUrl, const QString &phpScript,
201 QString urlParameters)
203 qDebug() << __PRETTY_FUNCTION__;
206 urlString.append(baseUrl);
207 urlString.append(phpScript);
208 if(!urlParameters.isEmpty())
209 urlString.append(urlParameters);
211 QUrl url = QUrl(urlString);
218 QString SituareService::formUrlParameters(const GeoCoordinate &coordinates, QString status,
221 qDebug() << __PRETTY_FUNCTION__;
223 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
224 // additional digit is added for maximum precision
225 const int COORDINATE_PRECISION = 10;
229 parameters.append(QUESTION_MARK);
230 parameters.append(LATITUDE);
231 parameters.append(EQUAL_MARK);
232 parameters.append(QString::number(coordinates.latitude(), 'f', COORDINATE_PRECISION));
233 parameters.append(AMBERSAND_MARK);
234 parameters.append(LONGTITUDE);
235 parameters.append(EQUAL_MARK);
236 parameters.append(QString::number(coordinates.longitude(), 'f', COORDINATE_PRECISION));
238 parameters.append(AMBERSAND_MARK);
239 parameters.append(PUBLISH);
240 parameters.append(EQUAL_MARK);
243 parameters.append(PUBLISH_TRUE);
245 parameters.append(PUBLISH_FALSE);
247 if(!status.isEmpty()) {
248 parameters.append(AMBERSAND_MARK);
249 parameters.append(DATA);
250 parameters.append(EQUAL_MARK);
251 parameters.append(status);
257 void SituareService::sendMessage(const QString &receiverId, const QString &message,
258 const GeoCoordinate &coordinates)
260 qDebug() << __PRETTY_FUNCTION__;
262 if (m_database->sendMessage(613374451, receiverId.toULongLong(), message, coordinates))
263 emit updateWasSuccessful(SituareService::SuccessfulSendMessage);
266 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
268 qDebug() << __PRETTY_FUNCTION__;
270 QNetworkRequest request;
273 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
274 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
276 QNetworkReply *reply = m_networkManager->get(request, true);
278 m_currentRequests.append(reply);
281 void SituareService::requestFinished(QNetworkReply *reply)
283 qDebug() << __PRETTY_FUNCTION__;
286 if (m_currentRequests.contains(reply)) {
288 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
290 if (reply->error()) {
291 emit error(ErrorContext::NETWORK, reply->error());
293 QByteArray replyArray = reply->readAll();
294 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
296 if(replyArray == ERROR_LAT.toAscii()) {
297 qDebug() << "Error: " << ERROR_LAT;
298 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
299 } else if(replyArray == ERROR_LON.toAscii()) {
300 qDebug() << "Error: " << ERROR_LON;
301 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
302 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
303 qDebug() << "Error: " << ERROR_SESSION;
304 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
305 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
306 qDebug() << "JSON string";
307 parseUserData(replyArray);
308 } else if(replyArray.isEmpty()) {
309 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
310 emit updateWasSuccessful(SituareService::SuccessfulUpdateLocation);
312 // session credentials are invalid
313 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
317 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
320 m_currentRequests.removeAll(reply);
321 reply->deleteLater();
325 void SituareService::credentialsReady(const FacebookCredentials &credentials)
327 qDebug() << __PRETTY_FUNCTION__;
329 m_credentials = credentials;
332 void SituareService::parseInterestingPeopleData(const QByteArray &jsonReply)
334 qDebug() << __PRETTY_FUNCTION__;
336 QJson::Parser parser;
339 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
342 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
345 QList<User> interestingPeople;
347 foreach (QVariant personVariant, result["people"].toList()) {
349 QMap<QString, QVariant> person = personVariant.toMap();
350 user.setUserId(person["uid"].toString());
351 user.setName(person["name"].toString());
352 user.setProfileImage(AvatarImage::create(
353 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
354 user.setProfileImageUrl(person["image_url"].toUrl());
355 user.setTags(person["tags"].toList());
356 interestingPeople.append(user);
358 //Remove comment when the actual server is used
359 //emit fetchImage(user.userId(), user.profileImageUrl());
362 emit interestingPeopleReceived(interestingPeople);
366 void SituareService::parseMessagesData(const QByteArray &jsonReply)
368 QJson::Parser parser;
371 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
374 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
377 QVariant messages = result["messages"];
379 QList<Message> received;
382 foreach (QVariant messageVariant, messages.toMap().value("received").toList()) {
383 Message message(Message::MessageTypeReceived);
384 QMap<QString, QVariant> messageMap = messageVariant.toMap();
385 message.setId(messageMap["id"].toString());
386 message.setSenderId(messageMap["sender_id"].toString());
387 message.setReceiverId(messageMap["receiver_id"].toString());
388 message.setSenderName(messageMap["sender_name"].toString());
389 uint timestampSeconds = messageMap["timestamp"].toUInt();
390 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
391 message.setText(messageMap["text"].toString());
392 message.setImage(AvatarImage::create(
393 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
396 qreal latitude = messageMap["latitude"].toReal(&latOk);
398 qreal longitude = messageMap["longitude"].toReal(&lonOk);
401 message.setCoordinates(GeoCoordinate(latitude, longitude));
403 received.append(message);
405 //emit fetchImage(message.id(), messageMap["image_url"].toString());
408 foreach (QVariant messageVariant, messages.toMap().value("sent").toList()) {
409 Message message(Message::MessageTypeSent);
410 QMap<QString, QVariant> messageMap = messageVariant.toMap();
411 message.setId(messageMap["id"].toString());
412 message.setSenderId(messageMap["sender_id"].toString());
413 message.setReceiverId(messageMap["receiver_id"].toString());
414 message.setSenderName(messageMap["sender_name"].toString());
415 uint timestampSeconds = messageMap["timestamp"].toUInt();
416 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
417 message.setText(messageMap["text"].toString());
418 message.setImage(AvatarImage::create(
419 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
422 qreal latitude = messageMap["latitude"].toReal(&latOk);
424 qreal longitude = messageMap["longitude"].toReal(&lonOk);
427 message.setCoordinates(GeoCoordinate(latitude, longitude));
429 sent.append(message);
431 //emit fetchImage(message.id(), messageMap["image_url"].toString());
434 emit messagesReceived(received, sent);
438 void SituareService::parseUserData(const QByteArray &jsonReply)
440 qDebug() << __PRETTY_FUNCTION__;
442 m_defaultImage = false;
444 QJson::Parser parser;
447 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
450 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
454 if(result.contains("ErrorCode")) {
455 QVariant errorVariant = result.value("ErrorCode");
456 emit error(ErrorContext::SITUARE, errorVariant.toInt());
458 } else if(result.contains("user")) {
460 QVariant userVariant = result.value("user");
461 QMap<QString, QVariant> userMap = userVariant.toMap();
463 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
465 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
467 QString address = userMap["address"].toString();
468 if(address.isEmpty()) {
469 QStringList location;
470 location.append(QString::number(coordinates.latitude()));
471 location.append(QString::number(coordinates.longitude()));
472 address = location.join(", ");
475 User user = User(address, coordinates, userMap["name"].toString(),
476 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
477 true, userMap["uid"].toString());
479 if(imageUrl.isEmpty()) {
480 // user doesn't have profile image, so we need to get him a silhouette image
481 m_defaultImage = true;
482 user.setProfileImage(AvatarImage::create(
483 QPixmap(":/res/images/empty_avatar_big.png"), AvatarImage::Large));
486 QList<User> tmpFriendsList;
488 foreach (QVariant friendsVariant, result["friends"].toList()) {
489 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
490 QVariant distance = friendMap["distance"];
491 QMap<QString, QVariant> distanceMap = distance.toMap();
493 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
495 QUrl imageUrl = friendMap["profile_pic"].toUrl();
497 QString address = friendMap["address"].toString();
498 if(address.isEmpty()) {
499 QStringList location;
500 location.append(QString::number(coordinates.latitude()));
501 location.append(QString::number(coordinates.longitude()));
502 address = location.join(", ");
505 User buddy = User(address, coordinates, friendMap["name"].toString(),
506 friendMap["note"].toString(), imageUrl,
507 friendMap["timestamp"].toString(),
508 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
509 distanceMap["value"].toDouble());
511 if(imageUrl.isEmpty()) {
512 // friend doesn't have profile image, so we need to get him a silhouette image
513 m_defaultImage = true;
514 buddy.setProfileImage(AvatarImage::create(
515 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
518 tmpFriendsList.append(buddy);
521 QHash<QString, QUrl> imageUrlList; // url list for images
523 // set unchanged profile images or add new images to imageUrlList for downloading
525 if(m_user->profileImageUrl() != user.profileImageUrl()) {
526 if(!user.profileImageUrl().isEmpty())
527 imageUrlList.insert(user.userId(), user.profileImageUrl());
529 user.setProfileImage(m_user->profileImage());
532 if(!user.profileImageUrl().isEmpty())
533 imageUrlList.insert(user.userId(), user.profileImageUrl());
536 // clear old user object
542 // create new user object from temporary user object
543 m_user = new User(user);
545 // set unchanged profile images or add new images to imageUrlList for downloading
546 if(!m_friendsList.isEmpty()) {
547 foreach(User tmpBuddy, tmpFriendsList) {
548 if(!tmpBuddy.profileImageUrl().isEmpty()) {
550 foreach(User *buddy, m_friendsList) {
551 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
552 tmpBuddy.setProfileImage(buddy->profileImage());
557 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
558 imageUrlList.insert(tmpBuddy.userId(), tmpBuddy.profileImageUrl());
562 foreach(User buddy, tmpFriendsList) {
563 if(!buddy.profileImageUrl().isEmpty())
564 imageUrlList.insert(buddy.userId(), buddy.profileImageUrl());
568 // clear old friendlist
569 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
570 m_friendsList.clear();
572 // populate new friendlist with temporary friendlist's data
573 foreach(User tmpFriendItem, tmpFriendsList) {
574 User *friendItem = new User(tmpFriendItem);
575 m_friendsList.append(friendItem);
577 tmpFriendsList.clear();
579 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
580 m_user->setTags(getTags(m_user->userId()));
582 emit userDataChanged(m_user, m_friendsList);
584 // set silhouette image to imageUrlList for downloading
586 imageUrlList.insert("", QUrl(SILHOUETTE_URL));
588 addProfileImages(imageUrlList);
589 imageUrlList.clear();
592 QVariant address = result.value("address");
593 if(!address.toString().isEmpty()) {
594 emit reverseGeoReady(address.toString());
596 QStringList coordinates;
597 coordinates.append(result.value("lat").toString());
598 coordinates.append(result.value("lon").toString());
600 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
601 emit reverseGeoReady(coordinates.join(", "));
607 void SituareService::imageReceived(const QString &id, const QPixmap &image)
609 qDebug() << __PRETTY_FUNCTION__;
611 if (m_user->userId() == id)
612 emit imageReady(id, AvatarImage::create(image, AvatarImage::Large));
614 emit imageReady(id, AvatarImage::create(image, AvatarImage::Small));
617 void SituareService::addProfileImages(const QHash<QString, QUrl> &imageUrlList)
619 qDebug() << __PRETTY_FUNCTION__;
621 QHashIterator<QString, QUrl> imageUrlListIterator(imageUrlList);
623 while (imageUrlListIterator.hasNext()) {
624 imageUrlListIterator.next();
625 emit fetchImage(imageUrlListIterator.key(), imageUrlListIterator.value());
629 void SituareService::clearUserData()
631 qDebug() << __PRETTY_FUNCTION__;
633 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
634 m_friendsList.clear();
640 emit userDataChanged(m_user, m_friendsList);
643 QHash<QString, QString> SituareService::getTags(const QString &userId)
645 qDebug() << __PRETTY_FUNCTION__;
647 return m_database->getTags(userId.toInt());
650 void SituareService::removeTags(const QStringList &tags)
652 qDebug() << __PRETTY_FUNCTION__;
654 if (m_database->removeTags(613374451, tags))
655 emit updateWasSuccessful(SituareService::SuccessfulRemoveTags);
658 void SituareService::removeMessage(const QString &id)
660 qDebug() << __PRETTY_FUNCTION__;
662 if (m_database->removeMessage(613374451, id))
663 emit updateWasSuccessful(SituareService::SuccessfulRemoveMessage);
666 void SituareService::addTags(const QStringList &tags)
668 qDebug() << __PRETTY_FUNCTION__;
672 foreach (QString tag, tags)
673 retVal = m_database->addTag(613374451, tag);
676 emit updateWasSuccessful(SituareService::SuccessfulAddTags);
679 void SituareService::searchPeopleByTag(const QString &tag)
681 qDebug() << __PRETTY_FUNCTION__;
683 QByteArray arr = m_database->getInterestingPeopleByTag(613374451, tag);
685 parseInterestingPeopleData(arr);