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 qWarning() << __PRETTY_FUNCTION__ << m_database->sendMessage(613374451,
263 receiverId.toULongLong(), message,
267 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
269 qDebug() << __PRETTY_FUNCTION__;
271 QNetworkRequest request;
274 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
275 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
277 QNetworkReply *reply = m_networkManager->get(request, true);
279 m_currentRequests.append(reply);
282 void SituareService::requestFinished(QNetworkReply *reply)
284 qDebug() << __PRETTY_FUNCTION__;
287 if (m_currentRequests.contains(reply)) {
289 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
291 if (reply->error()) {
292 emit error(ErrorContext::NETWORK, reply->error());
294 QByteArray replyArray = reply->readAll();
295 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
297 if(replyArray == ERROR_LAT.toAscii()) {
298 qDebug() << "Error: " << ERROR_LAT;
299 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
300 } else if(replyArray == ERROR_LON.toAscii()) {
301 qDebug() << "Error: " << ERROR_LON;
302 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
303 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
304 qDebug() << "Error: " << ERROR_SESSION;
305 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
306 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
307 qDebug() << "JSON string";
308 parseUserData(replyArray);
309 } else if(replyArray.isEmpty()) {
310 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
311 emit updateWasSuccessful(SituareService::SuccessfulUpdateLocation);
313 // session credentials are invalid
314 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
318 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
321 m_currentRequests.removeAll(reply);
322 reply->deleteLater();
326 void SituareService::credentialsReady(const FacebookCredentials &credentials)
328 qDebug() << __PRETTY_FUNCTION__;
330 m_credentials = credentials;
333 void SituareService::parseInterestingPeopleData(const QByteArray &jsonReply)
335 qDebug() << __PRETTY_FUNCTION__;
337 QJson::Parser parser;
340 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
343 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
346 QList<User> interestingPeople;
348 foreach (QVariant personVariant, result["people"].toList()) {
350 QMap<QString, QVariant> person = personVariant.toMap();
351 user.setUserId(person["uid"].toString());
352 user.setName(person["name"].toString());
353 user.setProfileImage(AvatarImage::create(
354 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
355 user.setProfileImageUrl(person["image_url"].toUrl());
356 user.setTags(person["tags"].toList());
357 interestingPeople.append(user);
359 //Remove comment when the actual server is used
360 //emit fetchImage(user.userId(), user.profileImageUrl());
363 emit interestingPeopleReceived(interestingPeople);
367 void SituareService::parseMessagesData(const QByteArray &jsonReply)
369 QJson::Parser parser;
372 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
375 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
378 QList<Message> messages;
380 foreach (QVariant messageVariant, result["messages"].toList()) {
382 QMap<QString, QVariant> messageMap = messageVariant.toMap();
383 message.setId(messageMap["id"].toString());
384 message.setSenderId(messageMap["sender_id"].toString());
385 message.setSenderName(messageMap["sender_name"].toString());
386 uint timestampSeconds = messageMap["timestamp"].toUInt();
387 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
388 message.setText(messageMap["text"].toString());
389 message.setImage(AvatarImage::create(
390 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
392 messages.append(message);
394 //emit fetchImage(message.id(), messageMap["image_url"].toString());
397 emit messagesReceived(messages);
401 void SituareService::parseUserData(const QByteArray &jsonReply)
403 qDebug() << __PRETTY_FUNCTION__;
405 m_defaultImage = false;
407 QJson::Parser parser;
410 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
413 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
417 if(result.contains("ErrorCode")) {
418 QVariant errorVariant = result.value("ErrorCode");
419 emit error(ErrorContext::SITUARE, errorVariant.toInt());
421 } else if(result.contains("user")) {
423 QVariant userVariant = result.value("user");
424 QMap<QString, QVariant> userMap = userVariant.toMap();
426 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
428 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
430 QString address = userMap["address"].toString();
431 if(address.isEmpty()) {
432 QStringList location;
433 location.append(QString::number(coordinates.latitude()));
434 location.append(QString::number(coordinates.longitude()));
435 address = location.join(", ");
438 User user = User(address, coordinates, userMap["name"].toString(),
439 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
440 true, userMap["uid"].toString());
442 if(imageUrl.isEmpty()) {
443 // user doesn't have profile image, so we need to get him a silhouette image
444 m_defaultImage = true;
445 user.setProfileImage(AvatarImage::create(
446 QPixmap(":/res/images/empty_avatar_big.png"), AvatarImage::Large));
449 QList<User> tmpFriendsList;
451 foreach (QVariant friendsVariant, result["friends"].toList()) {
452 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
453 QVariant distance = friendMap["distance"];
454 QMap<QString, QVariant> distanceMap = distance.toMap();
456 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
458 QUrl imageUrl = friendMap["profile_pic"].toUrl();
460 QString address = friendMap["address"].toString();
461 if(address.isEmpty()) {
462 QStringList location;
463 location.append(QString::number(coordinates.latitude()));
464 location.append(QString::number(coordinates.longitude()));
465 address = location.join(", ");
468 User buddy = User(address, coordinates, friendMap["name"].toString(),
469 friendMap["note"].toString(), imageUrl,
470 friendMap["timestamp"].toString(),
471 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
472 distanceMap["value"].toDouble());
474 if(imageUrl.isEmpty()) {
475 // friend doesn't have profile image, so we need to get him a silhouette image
476 m_defaultImage = true;
477 buddy.setProfileImage(AvatarImage::create(
478 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
481 tmpFriendsList.append(buddy);
484 QHash<QString, QUrl> imageUrlList; // url list for images
486 // set unchanged profile images or add new images to imageUrlList for downloading
488 if(m_user->profileImageUrl() != user.profileImageUrl()) {
489 if(!user.profileImageUrl().isEmpty())
490 imageUrlList.insert(user.userId(), user.profileImageUrl());
492 user.setProfileImage(m_user->profileImage());
495 if(!user.profileImageUrl().isEmpty())
496 imageUrlList.insert(user.userId(), user.profileImageUrl());
499 // clear old user object
505 // create new user object from temporary user object
506 m_user = new User(user);
508 // set unchanged profile images or add new images to imageUrlList for downloading
509 if(!m_friendsList.isEmpty()) {
510 foreach(User tmpBuddy, tmpFriendsList) {
511 if(!tmpBuddy.profileImageUrl().isEmpty()) {
513 foreach(User *buddy, m_friendsList) {
514 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
515 tmpBuddy.setProfileImage(buddy->profileImage());
520 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
521 imageUrlList.insert(tmpBuddy.userId(), tmpBuddy.profileImageUrl());
525 foreach(User buddy, tmpFriendsList) {
526 if(!buddy.profileImageUrl().isEmpty())
527 imageUrlList.insert(buddy.userId(), buddy.profileImageUrl());
531 // clear old friendlist
532 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
533 m_friendsList.clear();
535 // populate new friendlist with temporary friendlist's data
536 foreach(User tmpFriendItem, tmpFriendsList) {
537 User *friendItem = new User(tmpFriendItem);
538 m_friendsList.append(friendItem);
540 tmpFriendsList.clear();
542 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
543 m_user->setTags(getTags(m_user->userId()));
545 emit userDataChanged(m_user, m_friendsList);
547 // set silhouette image to imageUrlList for downloading
549 imageUrlList.insert("", QUrl(SILHOUETTE_URL));
551 addProfileImages(imageUrlList);
552 imageUrlList.clear();
555 QVariant address = result.value("address");
556 if(!address.toString().isEmpty()) {
557 emit reverseGeoReady(address.toString());
559 QStringList coordinates;
560 coordinates.append(result.value("lat").toString());
561 coordinates.append(result.value("lon").toString());
563 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
564 emit reverseGeoReady(coordinates.join(", "));
570 void SituareService::imageReceived(const QString &id, const QPixmap &image)
572 qDebug() << __PRETTY_FUNCTION__;
574 if (m_user->userId() == id)
575 emit imageReady(id, AvatarImage::create(image, AvatarImage::Large));
577 emit imageReady(id, AvatarImage::create(image, AvatarImage::Small));
580 void SituareService::addProfileImages(const QHash<QString, QUrl> &imageUrlList)
582 qDebug() << __PRETTY_FUNCTION__;
584 QHashIterator<QString, QUrl> imageUrlListIterator(imageUrlList);
586 while (imageUrlListIterator.hasNext()) {
587 imageUrlListIterator.next();
588 emit fetchImage(imageUrlListIterator.key(), imageUrlListIterator.value());
592 void SituareService::clearUserData()
594 qDebug() << __PRETTY_FUNCTION__;
596 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
597 m_friendsList.clear();
603 emit userDataChanged(m_user, m_friendsList);
606 QHash<QString, QString> SituareService::getTags(const QString &userId)
608 qDebug() << __PRETTY_FUNCTION__;
610 return m_database->getTags(userId.toInt());
613 void SituareService::removeTags(const QStringList &tags)
615 qDebug() << __PRETTY_FUNCTION__;
617 m_database->removeTags(613374451, tags);
620 void SituareService::removeMessage(const QString &id)
622 qDebug() << __PRETTY_FUNCTION__;
624 if (m_database->removeMessage(613374451, id))
625 emit updateWasSuccessful(SituareService::SuccessfulRemoveMessage);
628 void SituareService::addTags(const QStringList &tags)
630 qDebug() << __PRETTY_FUNCTION__;
632 foreach (QString tag, tags)
633 m_database->addTag(613374451, tag);
636 void SituareService::searchPeopleByTag(const QString &tag)
638 qDebug() << __PRETTY_FUNCTION__;
640 QByteArray arr = m_database->getInterestingPeopleByTag(613374451, tag);
642 parseInterestingPeopleData(arr);