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);
375 user.setCoordinates(GeoCoordinate(latitude, longitude));
377 friends.append(user);
379 //Remove comment when the actual server is used
380 //emit fetchImage(user.userId(), user.profileImageUrl());
383 foreach (QVariant personVariant, people.toMap().value("others").toList()) {
385 QMap<QString, QVariant> person = personVariant.toMap();
386 user.setUserId(person["uid"].toString());
387 user.setName(person["name"].toString());
388 user.setProfileImage(AvatarImage::create(
389 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
390 user.setProfileImageUrl(person["image_url"].toUrl());
391 user.setTags(person["tags"].toList());
395 //Remove comment when the actual server is used
396 //emit fetchImage(user.userId(), user.profileImageUrl());
399 emit interestingPeopleReceived(friends, others);
403 void SituareService::parseMessagesData(const QByteArray &jsonReply)
405 QJson::Parser parser;
408 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
411 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
414 QVariant messages = result["messages"];
416 QList<Message> received;
419 foreach (QVariant messageVariant, messages.toMap().value("received").toList()) {
420 Message message(Message::MessageTypeReceived);
421 QMap<QString, QVariant> messageMap = messageVariant.toMap();
422 message.setId(messageMap["id"].toString());
423 message.setSenderId(messageMap["sender_id"].toString());
424 message.setReceiverId(messageMap["receiver_id"].toString());
425 message.setSenderName(messageMap["sender_name"].toString());
426 uint timestampSeconds = messageMap["timestamp"].toUInt();
427 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
428 message.setText(messageMap["text"].toString());
429 message.setImage(AvatarImage::create(
430 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
433 qreal latitude = messageMap["latitude"].toReal(&latOk);
435 qreal longitude = messageMap["longitude"].toReal(&lonOk);
437 if (latOk && lonOk) {
438 message.setAddress(messageMap["address"].toString());
439 message.setCoordinates(GeoCoordinate(latitude, longitude));
442 received.append(message);
444 //emit fetchImage(message.id(), messageMap["image_url"].toString());
447 foreach (QVariant messageVariant, messages.toMap().value("sent").toList()) {
448 Message message(Message::MessageTypeSent);
449 QMap<QString, QVariant> messageMap = messageVariant.toMap();
450 message.setId(messageMap["id"].toString());
451 message.setSenderId(messageMap["sender_id"].toString());
452 message.setReceiverId(messageMap["receiver_id"].toString());
453 message.setSenderName(messageMap["sender_name"].toString());
454 uint timestampSeconds = messageMap["timestamp"].toUInt();
455 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
456 message.setText(messageMap["text"].toString());
457 message.setImage(AvatarImage::create(
458 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
461 qreal latitude = messageMap["latitude"].toReal(&latOk);
463 qreal longitude = messageMap["longitude"].toReal(&lonOk);
465 if (latOk && lonOk) {
466 message.setAddress(messageMap["address"].toString());
467 message.setCoordinates(GeoCoordinate(latitude, longitude));
470 sent.append(message);
472 //emit fetchImage(message.id(), messageMap["image_url"].toString());
475 emit messagesReceived(received, sent);
479 void SituareService::parsePopularTagsData(const QByteArray &jsonReply)
481 qDebug() << __PRETTY_FUNCTION__;
483 QJson::Parser parser;
486 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
489 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
492 QHash<QString, QString> popularTags;
494 foreach (QVariant tagVariant, result["popular_tags"].toList()) {
495 QMap<QString, QVariant> tag = tagVariant.toMap();
496 popularTags.insert(tag["id"].toString(), tag["name"].toString());
499 emit popularTagsReceived(popularTags);
504 void SituareService::parseUserData(const QByteArray &jsonReply)
506 qDebug() << __PRETTY_FUNCTION__;
508 m_defaultImage = false;
510 QJson::Parser parser;
513 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
516 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
520 if(result.contains("ErrorCode")) {
521 QVariant errorVariant = result.value("ErrorCode");
522 emit error(ErrorContext::SITUARE, errorVariant.toInt());
524 } else if(result.contains("user")) {
526 QVariant userVariant = result.value("user");
527 QMap<QString, QVariant> userMap = userVariant.toMap();
529 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
531 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
533 QString address = userMap["address"].toString();
534 if(address.isEmpty()) {
535 QStringList location;
536 location.append(QString::number(coordinates.latitude()));
537 location.append(QString::number(coordinates.longitude()));
538 address = location.join(", ");
541 User user = User(address, coordinates, userMap["name"].toString(),
542 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
543 true, userMap["uid"].toString());
545 if(imageUrl.isEmpty()) {
546 // user doesn't have profile image, so we need to get him a silhouette image
547 m_defaultImage = true;
548 user.setProfileImage(AvatarImage::create(
549 QPixmap(":/res/images/empty_avatar_big.png"), AvatarImage::Large));
552 QList<User> tmpFriendsList;
554 foreach (QVariant friendsVariant, result["friends"].toList()) {
555 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
556 QVariant distance = friendMap["distance"];
557 QMap<QString, QVariant> distanceMap = distance.toMap();
559 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
561 QUrl imageUrl = friendMap["profile_pic"].toUrl();
563 QString address = friendMap["address"].toString();
564 if(address.isEmpty()) {
565 QStringList location;
566 location.append(QString::number(coordinates.latitude()));
567 location.append(QString::number(coordinates.longitude()));
568 address = location.join(", ");
571 User buddy = User(address, coordinates, friendMap["name"].toString(),
572 friendMap["note"].toString(), imageUrl,
573 friendMap["timestamp"].toString(),
574 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
575 distanceMap["value"].toDouble());
577 if(imageUrl.isEmpty()) {
578 // friend doesn't have profile image, so we need to get him a silhouette image
579 m_defaultImage = true;
580 buddy.setProfileImage(AvatarImage::create(
581 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
584 tmpFriendsList.append(buddy);
587 QHash<QString, QUrl> imageUrlList; // url list for images
589 // set unchanged profile images or add new images to imageUrlList for downloading
591 if(m_user->profileImageUrl() != user.profileImageUrl()) {
592 if(!user.profileImageUrl().isEmpty())
593 imageUrlList.insert(user.userId(), user.profileImageUrl());
595 user.setProfileImage(m_user->profileImage());
598 if(!user.profileImageUrl().isEmpty())
599 imageUrlList.insert(user.userId(), user.profileImageUrl());
602 // clear old user object
608 // create new user object from temporary user object
609 m_user = new User(user);
611 // set unchanged profile images or add new images to imageUrlList for downloading
612 if(!m_friendsList.isEmpty()) {
613 foreach(User tmpBuddy, tmpFriendsList) {
614 if(!tmpBuddy.profileImageUrl().isEmpty()) {
616 foreach(User *buddy, m_friendsList) {
617 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
618 tmpBuddy.setProfileImage(buddy->profileImage());
623 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
624 imageUrlList.insert(tmpBuddy.userId(), tmpBuddy.profileImageUrl());
628 foreach(User buddy, tmpFriendsList) {
629 if(!buddy.profileImageUrl().isEmpty())
630 imageUrlList.insert(buddy.userId(), buddy.profileImageUrl());
634 // clear old friendlist
635 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
636 m_friendsList.clear();
638 // populate new friendlist with temporary friendlist's data
639 foreach(User tmpFriendItem, tmpFriendsList) {
640 User *friendItem = new User(tmpFriendItem);
641 m_friendsList.append(friendItem);
643 tmpFriendsList.clear();
645 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
646 m_user->setTags(getTags(m_user->userId()));
648 emit userDataChanged(m_user, m_friendsList);
650 // set silhouette image to imageUrlList for downloading
652 imageUrlList.insert("", QUrl(SILHOUETTE_URL));
654 addProfileImages(imageUrlList);
655 imageUrlList.clear();
658 QVariant address = result.value("address");
659 if(!address.toString().isEmpty()) {
660 emit reverseGeoReady(address.toString());
662 QStringList coordinates;
663 coordinates.append(result.value("lat").toString());
664 coordinates.append(result.value("lon").toString());
666 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
667 emit reverseGeoReady(coordinates.join(", "));
673 void SituareService::imageReceived(const QString &id, const QPixmap &image)
675 qDebug() << __PRETTY_FUNCTION__;
677 if (m_user->userId() == id)
678 emit imageReady(id, AvatarImage::create(image, AvatarImage::Large));
680 emit imageReady(id, AvatarImage::create(image, AvatarImage::Small));
683 void SituareService::addProfileImages(const QHash<QString, QUrl> &imageUrlList)
685 qDebug() << __PRETTY_FUNCTION__;
687 QHashIterator<QString, QUrl> imageUrlListIterator(imageUrlList);
689 while (imageUrlListIterator.hasNext()) {
690 imageUrlListIterator.next();
691 emit fetchImage(imageUrlListIterator.key(), imageUrlListIterator.value());
695 void SituareService::clearUserData()
697 qDebug() << __PRETTY_FUNCTION__;
699 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
700 m_friendsList.clear();
706 emit userDataChanged(m_user, m_friendsList);
709 QHash<QString, QString> SituareService::getTags(const QString &userId)
711 qDebug() << __PRETTY_FUNCTION__;
713 return m_database->getTags(userId.toInt());
716 void SituareService::removeTags(const QStringList &tags)
718 qDebug() << __PRETTY_FUNCTION__;
720 if (m_database->removeTags(613374451, tags))
721 emit updateWasSuccessful(SituareService::SuccessfulRemoveTags);
724 void SituareService::removeMessage(const QString &id)
726 qDebug() << __PRETTY_FUNCTION__;
728 if (m_database->removeMessage(613374451, id))
729 emit updateWasSuccessful(SituareService::SuccessfulRemoveMessage);
732 void SituareService::addTags(const QStringList &tags)
734 qDebug() << __PRETTY_FUNCTION__;
736 foreach (QString tag, tags)
737 m_database->addTag(613374451, tag);
739 emit updateWasSuccessful(SituareService::SuccessfulAddTags);
742 void SituareService::searchPeopleByTag(const QString &tag)
744 qDebug() << __PRETTY_FUNCTION__;
746 QByteArray arr = m_database->getInterestingPeopleByTag(613374451, tag);
748 parseInterestingPeopleData(arr);