2 Situare - A location system for Facebook
3 Copyright (C) 2010 Ixonos Plc. Authors:
5 Henri Lampela - henri.lampela@ixonos.com
6 Sami Rämö - sami.ramo@ixonos.com
8 Situare is free software; you can redistribute it and/or
9 modify it under the terms of the GNU General Public License
10 version 2 as published by the Free Software Foundation.
12 Situare is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with Situare; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
23 #include <qjson/parser.h>
26 #include <QtNetwork/QNetworkReply>
28 #include <QStringList>
29 #include <QtAlgorithms>
34 #include "network/networkaccessmanager.h"
35 #include "situarecommon.h"
36 #include "ui/avatarimage.h"
38 #include "situareservice.h"
40 SituareService::SituareService(NetworkAccessManager *networkManager, ImageFetcher *imageFetcher,
45 qDebug() << __PRETTY_FUNCTION__;
47 m_networkManager = networkManager;
48 connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
49 this, SLOT(requestFinished(QNetworkReply*)), Qt::QueuedConnection);
51 m_imageFetcher = imageFetcher;
52 connect(this, SIGNAL(fetchImage(QString, QUrl)),
53 m_imageFetcher, SLOT(fetchImage(QString, QUrl)));
54 connect(m_imageFetcher, SIGNAL(imageReceived(QString,QPixmap)),
55 this, SLOT(imageReceived(QString,QPixmap)));
56 connect(m_imageFetcher, SIGNAL(error(int, int)),
57 this, SIGNAL(error(int, int)));
59 m_database = new Database(this);
60 m_database->openDatabase();
61 m_database->createNotificationTable();
62 m_database->createUserTable();
63 m_database->createTagTable();
64 m_database->createUserTagTable();
67 SituareService::~SituareService()
69 qDebug() << __PRETTY_FUNCTION__;
76 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
77 m_friendsList.clear();
80 void SituareService::addProfileImages(const QHash<QString, QUrl> &imageUrlList)
82 qDebug() << __PRETTY_FUNCTION__;
84 QHashIterator<QString, QUrl> imageUrlListIterator(imageUrlList);
86 while (imageUrlListIterator.hasNext()) {
87 imageUrlListIterator.next();
88 emit fetchImage(imageUrlListIterator.key(), imageUrlListIterator.value());
92 void SituareService::addTags(const QStringList &tags)
94 qWarning() << __PRETTY_FUNCTION__ << tags.count();
96 foreach (QString tag, tags)
97 m_database->addTag(613374451, tag);
99 emit updateWasSuccessful(SituareService::SuccessfulAddTags);
102 void SituareService::appendAccessToken(QString &requestUrl)
104 qDebug() << __PRETTY_FUNCTION__;
106 requestUrl.append(m_session);
109 void SituareService::buildRequest(const QString &script, const QHash<QString, QString> ¶meters)
111 qDebug() << __PRETTY_FUNCTION__;
113 const QString PARAMETER_KEY_API = "api";
114 const QString PARAMETER_VALUE_API = "2.0";
116 QString url = SITUARE_URL;
120 // append default api version parameter if not yet specified
121 // if (!parameters.contains(PARAMETER_KEY_API))
122 // url.append(PARAMETER_KEY_API + "=" + PARAMETER_VALUE_API + "&");
125 if (!parameters.isEmpty()) {
126 QHash<QString, QString>::const_iterator i = parameters.constBegin();
127 while (i != parameters.constEnd()) {
130 url.append(i.value());
136 /// @todo BUG: Url parameter strings are not url escaped
138 // qWarning() << __PRETTY_FUNCTION__ << "request url with parameters:" << url;
140 if (!m_session.isEmpty()) {
141 appendAccessToken(url);
144 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
148 void SituareService::clearUserData()
150 qDebug() << __PRETTY_FUNCTION__;
152 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
153 m_friendsList.clear();
159 emit userDataChanged(m_user, m_friendsList);
162 QString SituareService::degreesToString(double degrees)
164 qDebug() << __PRETTY_FUNCTION__;
166 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
167 // additional digit is added for maximum precision
168 const int PRECISION = 10;
170 return QString::number(degrees, 'f', PRECISION);
173 void SituareService::fetchMessages()
175 qDebug() << __PRETTY_FUNCTION__;
177 //Request sent to server does not need the UID
178 QByteArray arr = m_database->getNotifications(613374451);
180 parseMessagesData(arr);
183 void SituareService::fetchPeopleWithSimilarInterest(const GeoCoordinate &southWestCoordinates,
184 const GeoCoordinate &northEastCoordinates)
186 qDebug() << __PRETTY_FUNCTION__;
188 //Request sent to server does not need the UID
189 QByteArray arr = m_database->getInterestingPeople(613374451,
190 southWestCoordinates,
191 northEastCoordinates);
193 parseInterestingPeopleData(arr);
196 void SituareService::fetchPopularTags()
198 qDebug() << __PRETTY_FUNCTION__;
200 QByteArray arr = m_database->getPopularTags();
202 parsePopularTagsData(arr);
205 void SituareService::fetchLocations()
207 qDebug() << __PRETTY_FUNCTION__;
209 QHash<QString, QString> parameters;
210 parameters.insert("extra_user_data", NORMAL_SIZE_PROFILE_IMAGE);
212 buildRequest(GET_LOCATIONS, parameters);
215 QHash<QString, QString> SituareService::getTags(const QString &userId)
217 qDebug() << __PRETTY_FUNCTION__;
219 return m_database->getTags(userId.toInt());
222 void SituareService::imageReceived(const QString &id, const QPixmap &image)
224 qDebug() << __PRETTY_FUNCTION__;
226 if (m_user->userId() == id)
227 emit imageReady(QString(), AvatarImage::create(image, AvatarImage::Large));
229 emit imageReady(id, AvatarImage::create(image, AvatarImage::Small));
232 void SituareService::parseInterestingPeopleData(const QByteArray &jsonReply)
234 qDebug() << __PRETTY_FUNCTION__;
236 QJson::Parser parser;
239 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
242 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
245 QVariant people = result["people"];
250 foreach (QVariant personVariant, people.toMap().value("friends").toList()) {
252 QMap<QString, QVariant> person = personVariant.toMap();
253 user.setUserId(person["uid"].toString());
254 user.setName(person["name"].toString());
255 user.setProfileImage(AvatarImage::create(
256 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
257 user.setProfileImageUrl(person["image_url"].toUrl());
258 user.setTags(person["tags"].toList());
261 qreal latitude = person["latitude"].toReal(&latOk);
263 qreal longitude = person["longitude"].toReal(&lonOk);
265 if (latOk && lonOk) {
266 user.setCoordinates(GeoCoordinate(latitude, longitude));
268 //This should be from the server
269 GeoCoordinate myCoord(65.008, 25.5523);
270 qreal meters = myCoord.distanceTo(user.coordinates());
280 user.setDistance(value, unit);
283 friends.append(user);
285 //Remove comment when the actual server is used
286 //emit fetchImage(user.userId(), user.profileImageUrl());
289 foreach (QVariant personVariant, people.toMap().value("others").toList()) {
291 QMap<QString, QVariant> person = personVariant.toMap();
292 user.setUserId(person["uid"].toString());
293 user.setName(person["name"].toString());
294 user.setProfileImage(AvatarImage::create(
295 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
296 user.setProfileImageUrl(person["image_url"].toUrl());
297 user.setTags(person["tags"].toList());
301 //Remove comment when the actual server is used
302 //emit fetchImage(user.userId(), user.profileImageUrl());
305 emit interestingPeopleReceived(friends, others);
309 void SituareService::parseUserData(const QByteArray &jsonReply)
311 qDebug() << __PRETTY_FUNCTION__;
313 m_defaultImage = false;
315 QJson::Parser parser;
318 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
320 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
324 if(result.contains("ErrorCode")) {
325 QVariant errorVariant = result.value("ErrorCode");
326 emit error(ErrorContext::SITUARE, errorVariant.toInt());
328 } else if(result.contains("user")) {
330 QVariant userVariant = result.value("user");
331 QMap<QString, QVariant> userMap = userVariant.toMap();
333 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
335 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
337 if(imageUrl.isEmpty()) {
338 // user doesn't have profile image, so we need to get him a silhouette image
339 m_defaultImage = true;
342 QString address = userMap["address"].toString();
343 if(address.isEmpty()) {
344 QStringList location;
345 location.append(QString::number(coordinates.latitude()));
346 location.append(QString::number(coordinates.longitude()));
347 address = location.join(", ");
350 User user = User(address, coordinates, userMap["name"].toString(),
351 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
352 true, userMap["uid"].toString());
354 if(imageUrl.isEmpty()) {
355 // user doesn't have profile image, so we need to get him a silhouette image
356 m_defaultImage = true;
357 user.setProfileImage(AvatarImage::create(
358 QPixmap(":/res/images/empty_avatar_big.png"), AvatarImage::Large));
361 QList<User> tmpFriendsList;
363 foreach (QVariant friendsVariant, result["friends"].toList()) {
364 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
365 QVariant distance = friendMap["distance"];
366 QMap<QString, QVariant> distanceMap = distance.toMap();
368 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
370 QUrl imageUrl = friendMap["profile_pic"].toUrl();
372 if(imageUrl.isEmpty()) {
373 // friend doesn't have profile image, so we need to get him a silhouette image
374 m_defaultImage = true;
377 QString address = friendMap["address"].toString();
378 if(address.isEmpty()) {
379 QStringList location;
380 location.append(QString::number(coordinates.latitude()));
381 location.append(QString::number(coordinates.longitude()));
382 address = location.join(", ");
385 User buddy = User(address, coordinates, friendMap["name"].toString(),
386 friendMap["note"].toString(), imageUrl,
387 friendMap["timestamp"].toString(),
388 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
389 distanceMap["value"].toDouble());
391 if(imageUrl.isEmpty()) {
392 // friend doesn't have profile image, so we need to get him a silhouette image
393 m_defaultImage = true;
394 buddy.setProfileImage(AvatarImage::create(
395 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
398 tmpFriendsList.append(buddy);
401 QHash<QString, QUrl> imageUrlList; // url list for images
403 // set unchanged profile images or add new images to imageUrlList for downloading
405 if(m_user->profileImageUrl() != user.profileImageUrl()) {
406 if(!user.profileImageUrl().isEmpty())
407 imageUrlList.insert(user.userId(), user.profileImageUrl());
409 user.setProfileImage(m_user->profileImage());
412 if(!user.profileImageUrl().isEmpty())
413 imageUrlList.insert(user.userId(), user.profileImageUrl());
416 // clear old user object
422 // create new user object from temporary user object
423 m_user = new User(user);
425 // set unchanged profile images or add new images to imageUrlList for downloading
426 if(!m_friendsList.isEmpty()) {
427 foreach(User tmpBuddy, tmpFriendsList) {
428 if(!tmpBuddy.profileImageUrl().isEmpty()) {
430 foreach(User *buddy, m_friendsList) {
431 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
432 tmpBuddy.setProfileImage(buddy->profileImage());
437 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
438 imageUrlList.insert(tmpBuddy.userId(), tmpBuddy.profileImageUrl());
442 foreach(User buddy, tmpFriendsList) {
443 if(!buddy.profileImageUrl().isEmpty())
444 imageUrlList.insert(buddy.userId(), buddy.profileImageUrl());
448 // clear old friendlist
449 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
450 m_friendsList.clear();
452 // populate new friendlist with temporary friendlist's data
453 foreach(User tmpFriendItem, tmpFriendsList) {
454 User *friendItem = new User(tmpFriendItem);
455 m_friendsList.append(friendItem);
457 tmpFriendsList.clear();
459 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
460 m_user->setTags(getTags(m_user->userId()));
462 emit userDataChanged(m_user, m_friendsList);
464 // set silhouette image to imageUrlList for downloading
466 imageUrlList.insert("", QUrl(SILHOUETTE_URL));
468 addProfileImages(imageUrlList);
469 imageUrlList.clear();
472 QVariant address = result.value("address");
473 if(!address.toString().isEmpty()) {
474 emit reverseGeoReady(address.toString());
476 QStringList coordinates;
477 coordinates.append(result.value("lat").toString());
478 coordinates.append(result.value("lon").toString());
480 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
481 emit reverseGeoReady(coordinates.join(", "));
487 void SituareService::parseMessagesData(const QByteArray &jsonReply)
489 QJson::Parser parser;
492 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
495 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
498 QVariant messages = result["messages"];
500 QList<Message> received;
503 foreach (QVariant messageVariant, messages.toMap().value("received").toList()) {
504 Message message(Message::MessageTypeReceived);
505 QMap<QString, QVariant> messageMap = messageVariant.toMap();
506 message.setId(messageMap["id"].toString());
507 message.setSenderId(messageMap["sender_id"].toString());
508 message.setReceiverId(messageMap["receiver_id"].toString());
509 message.setSenderName(messageMap["sender_name"].toString());
510 uint timestampSeconds = messageMap["timestamp"].toUInt();
511 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
512 message.setText(messageMap["text"].toString());
513 message.setImage(AvatarImage::create(
514 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
517 qreal latitude = messageMap["latitude"].toReal(&latOk);
519 qreal longitude = messageMap["longitude"].toReal(&lonOk);
521 if (latOk && lonOk) {
522 message.setAddress(messageMap["address"].toString());
523 message.setCoordinates(GeoCoordinate(latitude, longitude));
526 received.append(message);
528 //emit fetchImage(message.id(), messageMap["image_url"].toString());
531 foreach (QVariant messageVariant, messages.toMap().value("sent").toList()) {
532 Message message(Message::MessageTypeSent);
533 QMap<QString, QVariant> messageMap = messageVariant.toMap();
534 message.setId(messageMap["id"].toString());
535 message.setSenderId(messageMap["sender_id"].toString());
536 message.setReceiverId(messageMap["receiver_id"].toString());
537 message.setSenderName(messageMap["sender_name"].toString());
538 uint timestampSeconds = messageMap["timestamp"].toUInt();
539 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
540 message.setText(messageMap["text"].toString());
541 message.setImage(AvatarImage::create(
542 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
545 qreal latitude = messageMap["latitude"].toReal(&latOk);
547 qreal longitude = messageMap["longitude"].toReal(&lonOk);
549 if (latOk && lonOk) {
550 message.setAddress(messageMap["address"].toString());
551 message.setCoordinates(GeoCoordinate(latitude, longitude));
554 sent.append(message);
556 //emit fetchImage(message.id(), messageMap["image_url"].toString());
559 emit messagesReceived(received, sent);
563 void SituareService::parsePopularTagsData(const QByteArray &jsonReply)
565 qDebug() << __PRETTY_FUNCTION__;
567 QJson::Parser parser;
570 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
573 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
576 QHash<QString, QString> popularTags;
578 foreach (QVariant tagVariant, result["popular_tags"].toList()) {
579 QMap<QString, QVariant> tag = tagVariant.toMap();
580 popularTags.insert(tag["id"].toString(), tag["name"].toString());
583 emit popularTagsReceived(popularTags);
587 void SituareService::removeMessage(const QString &id)
589 qDebug() << __PRETTY_FUNCTION__;
591 if (m_database->removeMessage(613374451, id))
592 emit updateWasSuccessful(SituareService::SuccessfulRemoveMessage);
596 void SituareService::removeTags(const QStringList &tags)
598 qDebug() << __PRETTY_FUNCTION__;
600 if (m_database->removeTags(613374451, tags))
601 emit updateWasSuccessful(SituareService::SuccessfulRemoveTags);
604 void SituareService::requestFinished(QNetworkReply *reply)
606 qDebug() << __PRETTY_FUNCTION__;
609 if (m_currentRequests.contains(reply)) {
611 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
613 if (reply->error()) {
614 emit error(ErrorContext::NETWORK, reply->error());
616 QByteArray replyArray = reply->readAll();
617 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
619 if(replyArray == ERROR_LAT.toAscii()) {
620 qDebug() << "Error: " << ERROR_LAT;
621 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
622 } else if(replyArray == ERROR_LON.toAscii()) {
623 qDebug() << "Error: " << ERROR_LON;
624 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
625 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
626 qDebug() << "Error: " << ERROR_SESSION;
627 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
628 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
629 qDebug() << "JSON string";
630 parseUserData(replyArray);
631 } else if(replyArray.isEmpty()) {
632 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
633 emit updateWasSuccessful(SituareService::SuccessfulUpdateLocation);
635 // session credentials are invalid
636 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
640 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
643 m_currentRequests.removeAll(reply);
644 reply->deleteLater();
648 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
650 qDebug() << __PRETTY_FUNCTION__;
652 QHash<QString, QString> parameters;
653 parameters.insert("lat", degreesToString(coordinates.latitude()));
654 parameters.insert("lon", degreesToString(coordinates.longitude()));
655 parameters.insert("format", "json");
657 buildRequest(REVERSE_GEO, parameters);
660 void SituareService::searchPeopleByTag(const QString &tag)
662 qDebug() << __PRETTY_FUNCTION__;
664 QByteArray arr = m_database->getInterestingPeopleByTag(613374451, tag);
666 parseInterestingPeopleData(arr);
669 void SituareService::sendMessage(const QString &receiverId, const QString &message,
670 const GeoCoordinate &coordinates)
672 qDebug() << __PRETTY_FUNCTION__;
674 if (m_database->sendMessage(613374451, receiverId.toULongLong(), message, coordinates))
675 emit updateWasSuccessful(SituareService::SuccessfulSendMessage);
678 void SituareService::sendRequest(const QString &requestUrl)
680 qDebug() << __PRETTY_FUNCTION__ << "requestUrl" << requestUrl;
682 // make and send the request
683 QNetworkRequest request;
684 request.setUrl(QUrl(requestUrl));
685 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
686 QNetworkReply *reply = m_networkManager->get(request, true);
687 m_currentRequests.append(reply);
690 void SituareService::updateSession(const QString &session)
692 qDebug() << __PRETTY_FUNCTION__;
696 if (m_session.isEmpty())
700 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
703 qDebug() << __PRETTY_FUNCTION__;
705 QHash<QString, QString> parameters;
706 parameters.insert("lat", degreesToString(coordinates.latitude()));
707 parameters.insert("lon", degreesToString(coordinates.longitude()));
708 parameters.insert("publish", publish ? "true" : "false");
709 parameters.insert("data", status); ///< @todo if !empty ???
711 buildRequest(UPDATE_LOCATION, parameters);