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::fetchPopularTags()
104 qDebug() << __PRETTY_FUNCTION__;
106 QByteArray arr = m_database->getPopularTags();
108 parsePopularTagsData(arr);
111 void SituareService::fetchLocations()
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 QUrl url = formUrl(SITUARE_URL, GET_LOCATIONS);
120 sendRequest(url, COOKIE, cookie);
123 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
125 qDebug() << __PRETTY_FUNCTION__;
127 QString cookie = formCookie(API_KEY, m_credentials.expires(),m_credentials.userID(),
128 m_credentials.sessionKey(), m_credentials.sessionSecret(),
129 m_credentials.sig(), EN_LOCALE);
131 QString urlParameters = formUrlParameters(coordinates);
132 urlParameters.append(JSON_FORMAT);
133 QUrl url = formUrl(SITUARE_URL, REVERSE_GEO, urlParameters);
135 sendRequest(url, COOKIE, cookie);
138 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
141 qDebug() << __PRETTY_FUNCTION__;
143 QString urlParameters = formUrlParameters(coordinates, status, publish);
144 QUrl url = formUrl(SITUARE_URL, UPDATE_LOCATION, urlParameters);
146 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
147 m_credentials.sessionKey(), m_credentials.sessionSecret(),
148 m_credentials.sig(), EN_LOCALE);
150 sendRequest(url, COOKIE, cookie);
153 QString SituareService::formCookie(const QString &apiKeyValue, QString expiresValue,
154 QString userValue, QString sessionKeyValue,
155 QString sessionSecretValue, const QString &signatureValue,
156 const QString &localeValue)
158 qDebug() << __PRETTY_FUNCTION__;
165 QString sessionSecret;
168 QString signature = EQUAL_MARK;
169 QStringList variableList;
171 signature.append(signatureValue);
172 apiKey.append(apiKeyValue);
173 apiKey.append(UNDERLINE_MARK);
176 user.append(EQUAL_MARK);
177 expires.append(EXPIRES);
178 expires.append(EQUAL_MARK);
179 sessionKey.append(SESSION_KEY);
180 sessionKey.append(EQUAL_MARK);
181 sessionSecret.append(SESSION_SECRET);
182 sessionSecret.append(EQUAL_MARK);
183 locale.append(LOCALE);
184 locale.append(EQUAL_MARK);
185 locale.append(localeValue);
187 variableList.append(expires.append(expiresValue.append(BREAK_MARK)));
188 variableList.append(sessionKey.append(sessionKeyValue.append(BREAK_MARK)));
189 variableList.append(user.append(userValue).append(BREAK_MARK));
190 variableList.append(sessionSecret.append(sessionSecretValue.append(BREAK_MARK)));
192 cookie.append(BREAK_MARK);
194 foreach(variable, variableList) {
195 cookie.append(apiKey);
196 cookie.append(variable);
198 apiKey.remove(UNDERLINE_MARK);
199 cookie.append(apiKey);
200 cookie.append(signature);
201 cookie.append(BREAK_MARK);
202 cookie.append(locale);
209 QUrl SituareService::formUrl(const QString &baseUrl, const QString &phpScript,
210 QString urlParameters)
212 qDebug() << __PRETTY_FUNCTION__;
215 urlString.append(baseUrl);
216 urlString.append(phpScript);
217 if(!urlParameters.isEmpty())
218 urlString.append(urlParameters);
220 QUrl url = QUrl(urlString);
227 QString SituareService::formUrlParameters(const GeoCoordinate &coordinates, QString status,
230 qDebug() << __PRETTY_FUNCTION__;
232 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
233 // additional digit is added for maximum precision
234 const int COORDINATE_PRECISION = 10;
238 parameters.append(QUESTION_MARK);
239 parameters.append(LATITUDE);
240 parameters.append(EQUAL_MARK);
241 parameters.append(QString::number(coordinates.latitude(), 'f', COORDINATE_PRECISION));
242 parameters.append(AMBERSAND_MARK);
243 parameters.append(LONGTITUDE);
244 parameters.append(EQUAL_MARK);
245 parameters.append(QString::number(coordinates.longitude(), 'f', COORDINATE_PRECISION));
247 parameters.append(AMBERSAND_MARK);
248 parameters.append(PUBLISH);
249 parameters.append(EQUAL_MARK);
252 parameters.append(PUBLISH_TRUE);
254 parameters.append(PUBLISH_FALSE);
256 if(!status.isEmpty()) {
257 parameters.append(AMBERSAND_MARK);
258 parameters.append(DATA);
259 parameters.append(EQUAL_MARK);
260 parameters.append(status);
266 void SituareService::sendMessage(const QString &receiverId, const QString &message,
267 const GeoCoordinate &coordinates)
269 qDebug() << __PRETTY_FUNCTION__;
271 if (m_database->sendMessage(613374451, receiverId.toULongLong(), message, coordinates))
272 emit updateWasSuccessful(SituareService::SuccessfulSendMessage);
275 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
277 qDebug() << __PRETTY_FUNCTION__;
279 QNetworkRequest request;
282 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
283 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
285 QNetworkReply *reply = m_networkManager->get(request, true);
287 m_currentRequests.append(reply);
290 void SituareService::requestFinished(QNetworkReply *reply)
292 qDebug() << __PRETTY_FUNCTION__;
295 if (m_currentRequests.contains(reply)) {
297 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
299 if (reply->error()) {
300 emit error(ErrorContext::NETWORK, reply->error());
302 QByteArray replyArray = reply->readAll();
303 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
305 if(replyArray == ERROR_LAT.toAscii()) {
306 qDebug() << "Error: " << ERROR_LAT;
307 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
308 } else if(replyArray == ERROR_LON.toAscii()) {
309 qDebug() << "Error: " << ERROR_LON;
310 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
311 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
312 qDebug() << "Error: " << ERROR_SESSION;
313 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
314 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
315 qDebug() << "JSON string";
316 parseUserData(replyArray);
317 } else if(replyArray.isEmpty()) {
318 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
319 emit updateWasSuccessful(SituareService::SuccessfulUpdateLocation);
321 // session credentials are invalid
322 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
326 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
329 m_currentRequests.removeAll(reply);
330 reply->deleteLater();
334 void SituareService::credentialsReady(const FacebookCredentials &credentials)
336 qDebug() << __PRETTY_FUNCTION__;
338 m_credentials = credentials;
341 void SituareService::parseInterestingPeopleData(const QByteArray &jsonReply)
343 qDebug() << __PRETTY_FUNCTION__;
345 QJson::Parser parser;
348 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
351 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
354 QVariant people = result["people"];
359 foreach (QVariant personVariant, people.toMap().value("friends").toList()) {
361 QMap<QString, QVariant> person = personVariant.toMap();
362 user.setUserId(person["uid"].toString());
363 user.setName(person["name"].toString());
364 user.setProfileImage(AvatarImage::create(
365 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
366 user.setProfileImageUrl(person["image_url"].toUrl());
367 user.setTags(person["tags"].toList());
370 qreal latitude = person["latitude"].toReal(&latOk);
372 qreal longitude = person["longitude"].toReal(&lonOk);
374 if (latOk && lonOk) {
375 user.setCoordinates(GeoCoordinate(latitude, longitude));
377 //This should be from the server
378 GeoCoordinate myCoord(65.008, 25.5523);
379 qreal meters = myCoord.distanceTo(user.coordinates());
389 user.setDistance(value, unit);
392 friends.append(user);
394 //Remove comment when the actual server is used
395 //emit fetchImage(user.userId(), user.profileImageUrl());
398 foreach (QVariant personVariant, people.toMap().value("others").toList()) {
400 QMap<QString, QVariant> person = personVariant.toMap();
401 user.setUserId(person["uid"].toString());
402 user.setName(person["name"].toString());
403 user.setProfileImage(AvatarImage::create(
404 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
405 user.setProfileImageUrl(person["image_url"].toUrl());
406 user.setTags(person["tags"].toList());
410 //Remove comment when the actual server is used
411 //emit fetchImage(user.userId(), user.profileImageUrl());
414 emit interestingPeopleReceived(friends, others);
418 void SituareService::parseMessagesData(const QByteArray &jsonReply)
420 QJson::Parser parser;
423 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
426 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
429 QVariant messages = result["messages"];
431 QList<Message> received;
434 foreach (QVariant messageVariant, messages.toMap().value("received").toList()) {
435 Message message(Message::MessageTypeReceived);
436 QMap<QString, QVariant> messageMap = messageVariant.toMap();
437 message.setId(messageMap["id"].toString());
438 message.setSenderId(messageMap["sender_id"].toString());
439 message.setReceiverId(messageMap["receiver_id"].toString());
440 message.setSenderName(messageMap["sender_name"].toString());
441 uint timestampSeconds = messageMap["timestamp"].toUInt();
442 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
443 message.setText(messageMap["text"].toString());
444 message.setImage(AvatarImage::create(
445 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
448 qreal latitude = messageMap["latitude"].toReal(&latOk);
450 qreal longitude = messageMap["longitude"].toReal(&lonOk);
452 if (latOk && lonOk) {
453 message.setAddress(messageMap["address"].toString());
454 message.setCoordinates(GeoCoordinate(latitude, longitude));
457 received.append(message);
459 //emit fetchImage(message.id(), messageMap["image_url"].toString());
462 foreach (QVariant messageVariant, messages.toMap().value("sent").toList()) {
463 Message message(Message::MessageTypeSent);
464 QMap<QString, QVariant> messageMap = messageVariant.toMap();
465 message.setId(messageMap["id"].toString());
466 message.setSenderId(messageMap["sender_id"].toString());
467 message.setReceiverId(messageMap["receiver_id"].toString());
468 message.setSenderName(messageMap["sender_name"].toString());
469 uint timestampSeconds = messageMap["timestamp"].toUInt();
470 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
471 message.setText(messageMap["text"].toString());
472 message.setImage(AvatarImage::create(
473 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
476 qreal latitude = messageMap["latitude"].toReal(&latOk);
478 qreal longitude = messageMap["longitude"].toReal(&lonOk);
480 if (latOk && lonOk) {
481 message.setAddress(messageMap["address"].toString());
482 message.setCoordinates(GeoCoordinate(latitude, longitude));
485 sent.append(message);
487 //emit fetchImage(message.id(), messageMap["image_url"].toString());
490 emit messagesReceived(received, sent);
494 void SituareService::parsePopularTagsData(const QByteArray &jsonReply)
496 qDebug() << __PRETTY_FUNCTION__;
498 QJson::Parser parser;
501 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
504 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
507 QHash<QString, QString> popularTags;
509 foreach (QVariant tagVariant, result["popular_tags"].toList()) {
510 QMap<QString, QVariant> tag = tagVariant.toMap();
511 popularTags.insert(tag["id"].toString(), tag["name"].toString());
514 emit popularTagsReceived(popularTags);
519 void SituareService::parseUserData(const QByteArray &jsonReply)
521 qDebug() << __PRETTY_FUNCTION__;
523 m_defaultImage = false;
525 QJson::Parser parser;
528 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
531 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
535 if(result.contains("ErrorCode")) {
536 QVariant errorVariant = result.value("ErrorCode");
537 emit error(ErrorContext::SITUARE, errorVariant.toInt());
539 } else if(result.contains("user")) {
541 QVariant userVariant = result.value("user");
542 QMap<QString, QVariant> userMap = userVariant.toMap();
544 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
546 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
548 QString address = userMap["address"].toString();
549 if(address.isEmpty()) {
550 QStringList location;
551 location.append(QString::number(coordinates.latitude()));
552 location.append(QString::number(coordinates.longitude()));
553 address = location.join(", ");
556 User user = User(address, coordinates, userMap["name"].toString(),
557 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
558 true, userMap["uid"].toString());
560 if(imageUrl.isEmpty()) {
561 // user doesn't have profile image, so we need to get him a silhouette image
562 m_defaultImage = true;
563 user.setProfileImage(AvatarImage::create(
564 QPixmap(":/res/images/empty_avatar_big.png"), AvatarImage::Large));
567 QList<User> tmpFriendsList;
569 foreach (QVariant friendsVariant, result["friends"].toList()) {
570 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
571 QVariant distance = friendMap["distance"];
572 QMap<QString, QVariant> distanceMap = distance.toMap();
574 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
576 QUrl imageUrl = friendMap["profile_pic"].toUrl();
578 QString address = friendMap["address"].toString();
579 if(address.isEmpty()) {
580 QStringList location;
581 location.append(QString::number(coordinates.latitude()));
582 location.append(QString::number(coordinates.longitude()));
583 address = location.join(", ");
586 User buddy = User(address, coordinates, friendMap["name"].toString(),
587 friendMap["note"].toString(), imageUrl,
588 friendMap["timestamp"].toString(),
589 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
590 distanceMap["value"].toDouble());
592 if(imageUrl.isEmpty()) {
593 // friend doesn't have profile image, so we need to get him a silhouette image
594 m_defaultImage = true;
595 buddy.setProfileImage(AvatarImage::create(
596 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
599 tmpFriendsList.append(buddy);
602 QHash<QString, QUrl> imageUrlList; // url list for images
604 // set unchanged profile images or add new images to imageUrlList for downloading
606 if(m_user->profileImageUrl() != user.profileImageUrl()) {
607 if(!user.profileImageUrl().isEmpty())
608 imageUrlList.insert(user.userId(), user.profileImageUrl());
610 user.setProfileImage(m_user->profileImage());
613 if(!user.profileImageUrl().isEmpty())
614 imageUrlList.insert(user.userId(), user.profileImageUrl());
617 // clear old user object
623 // create new user object from temporary user object
624 m_user = new User(user);
626 // set unchanged profile images or add new images to imageUrlList for downloading
627 if(!m_friendsList.isEmpty()) {
628 foreach(User tmpBuddy, tmpFriendsList) {
629 if(!tmpBuddy.profileImageUrl().isEmpty()) {
631 foreach(User *buddy, m_friendsList) {
632 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
633 tmpBuddy.setProfileImage(buddy->profileImage());
638 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
639 imageUrlList.insert(tmpBuddy.userId(), tmpBuddy.profileImageUrl());
643 foreach(User buddy, tmpFriendsList) {
644 if(!buddy.profileImageUrl().isEmpty())
645 imageUrlList.insert(buddy.userId(), buddy.profileImageUrl());
649 // clear old friendlist
650 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
651 m_friendsList.clear();
653 // populate new friendlist with temporary friendlist's data
654 foreach(User tmpFriendItem, tmpFriendsList) {
655 User *friendItem = new User(tmpFriendItem);
656 m_friendsList.append(friendItem);
658 tmpFriendsList.clear();
660 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
661 m_user->setTags(getTags(m_user->userId()));
663 emit userDataChanged(m_user, m_friendsList);
665 // set silhouette image to imageUrlList for downloading
667 imageUrlList.insert("", QUrl(SILHOUETTE_URL));
669 addProfileImages(imageUrlList);
670 imageUrlList.clear();
673 QVariant address = result.value("address");
674 if(!address.toString().isEmpty()) {
675 emit reverseGeoReady(address.toString());
677 QStringList coordinates;
678 coordinates.append(result.value("lat").toString());
679 coordinates.append(result.value("lon").toString());
681 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
682 emit reverseGeoReady(coordinates.join(", "));
688 void SituareService::imageReceived(const QString &id, const QPixmap &image)
690 qDebug() << __PRETTY_FUNCTION__;
692 if (m_user->userId() == id)
693 emit imageReady(id, AvatarImage::create(image, AvatarImage::Large));
695 emit imageReady(id, AvatarImage::create(image, AvatarImage::Small));
698 void SituareService::addProfileImages(const QHash<QString, QUrl> &imageUrlList)
700 qDebug() << __PRETTY_FUNCTION__;
702 QHashIterator<QString, QUrl> imageUrlListIterator(imageUrlList);
704 while (imageUrlListIterator.hasNext()) {
705 imageUrlListIterator.next();
706 emit fetchImage(imageUrlListIterator.key(), imageUrlListIterator.value());
710 void SituareService::clearUserData()
712 qDebug() << __PRETTY_FUNCTION__;
714 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
715 m_friendsList.clear();
721 emit userDataChanged(m_user, m_friendsList);
724 QHash<QString, QString> SituareService::getTags(const QString &userId)
726 qDebug() << __PRETTY_FUNCTION__;
728 return m_database->getTags(userId.toInt());
731 void SituareService::removeTags(const QStringList &tags)
733 qDebug() << __PRETTY_FUNCTION__;
735 if (m_database->removeTags(613374451, tags))
736 emit updateWasSuccessful(SituareService::SuccessfulRemoveTags);
739 void SituareService::removeMessage(const QString &id)
741 qDebug() << __PRETTY_FUNCTION__;
743 if (m_database->removeMessage(613374451, id))
744 emit updateWasSuccessful(SituareService::SuccessfulRemoveMessage);
747 void SituareService::addTags(const QStringList &tags)
749 qWarning() << __PRETTY_FUNCTION__ << tags.count();
751 foreach (QString tag, tags)
752 m_database->addTag(613374451, tag);
754 emit updateWasSuccessful(SituareService::SuccessfulAddTags);
757 void SituareService::searchPeopleByTag(const QString &tag)
759 qDebug() << __PRETTY_FUNCTION__;
761 QByteArray arr = m_database->getInterestingPeopleByTag(613374451, tag);
763 parseInterestingPeopleData(arr);