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)
259 qDebug() << __PRETTY_FUNCTION__;
261 qWarning() << __PRETTY_FUNCTION__ << m_database->sendMessage(613374451, receiverId.toULongLong(), message);
264 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
266 qDebug() << __PRETTY_FUNCTION__;
268 QNetworkRequest request;
271 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
272 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
274 QNetworkReply *reply = m_networkManager->get(request, true);
276 m_currentRequests.append(reply);
279 void SituareService::requestFinished(QNetworkReply *reply)
281 qDebug() << __PRETTY_FUNCTION__;
284 if (m_currentRequests.contains(reply)) {
286 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
288 if (reply->error()) {
289 emit error(ErrorContext::NETWORK, reply->error());
291 QByteArray replyArray = reply->readAll();
292 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
294 if(replyArray == ERROR_LAT.toAscii()) {
295 qDebug() << "Error: " << ERROR_LAT;
296 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
297 } else if(replyArray == ERROR_LON.toAscii()) {
298 qDebug() << "Error: " << ERROR_LON;
299 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
300 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
301 qDebug() << "Error: " << ERROR_SESSION;
302 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
303 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
304 qDebug() << "JSON string";
305 parseUserData(replyArray);
306 } else if(replyArray.isEmpty()) {
307 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
308 emit updateWasSuccessful();
310 // session credentials are invalid
311 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
315 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
318 m_currentRequests.removeAll(reply);
319 reply->deleteLater();
323 void SituareService::credentialsReady(const FacebookCredentials &credentials)
325 qDebug() << __PRETTY_FUNCTION__;
327 m_credentials = credentials;
330 void SituareService::parseInterestingPeopleData(const QByteArray &jsonReply)
332 qDebug() << __PRETTY_FUNCTION__;
334 QJson::Parser parser;
337 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
340 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
343 QList<User> interestingPeople;
345 foreach (QVariant personVariant, result["people"].toList()) {
347 QMap<QString, QVariant> person = personVariant.toMap();
348 user.setUserId(person["uid"].toString());
349 user.setName(person["name"].toString());
350 user.setProfileImage(AvatarImage::create(
351 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
352 user.setProfileImageUrl(person["image_url"].toUrl());
353 user.setTags(person["tags"].toList());
354 interestingPeople.append(user);
356 //Remove comment when the actual server is used
357 //emit fetchImage(user.userId(), user.profileImageUrl());
360 emit interestingPeopleReceived(interestingPeople);
364 void SituareService::parseMessagesData(const QByteArray &jsonReply)
366 QJson::Parser parser;
369 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
372 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
375 QList<Message> messages;
377 foreach (QVariant messageVariant, result["messages"].toList()) {
379 QMap<QString, QVariant> messageMap = messageVariant.toMap();
380 message.setId(messageMap["id"].toString());
381 message.setSenderId(messageMap["sender_id"].toString());
382 message.setSenderName(messageMap["sender_name"].toString());
383 uint timestampSeconds = messageMap["timestamp"].toUInt();
384 message.setTimestamp(QDateTime::fromTime_t(timestampSeconds));
385 message.setText(messageMap["text"].toString());
386 message.setImage(AvatarImage::create(
387 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
389 messages.append(message);
391 //emit fetchImage(message.id(), messageMap["image_url"].toString());
394 emit messagesReceived(messages);
398 void SituareService::parseUserData(const QByteArray &jsonReply)
400 qDebug() << __PRETTY_FUNCTION__;
402 m_defaultImage = false;
404 QJson::Parser parser;
407 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
410 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
414 if(result.contains("ErrorCode")) {
415 QVariant errorVariant = result.value("ErrorCode");
416 emit error(ErrorContext::SITUARE, errorVariant.toInt());
418 } else if(result.contains("user")) {
420 QVariant userVariant = result.value("user");
421 QMap<QString, QVariant> userMap = userVariant.toMap();
423 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
425 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
427 QString address = userMap["address"].toString();
428 if(address.isEmpty()) {
429 QStringList location;
430 location.append(QString::number(coordinates.latitude()));
431 location.append(QString::number(coordinates.longitude()));
432 address = location.join(", ");
435 User user = User(address, coordinates, userMap["name"].toString(),
436 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
437 true, userMap["uid"].toString());
439 if(imageUrl.isEmpty()) {
440 // user doesn't have profile image, so we need to get him a silhouette image
441 m_defaultImage = true;
442 user.setProfileImage(AvatarImage::create(
443 QPixmap(":/res/images/empty_avatar_big.png"), AvatarImage::Large));
446 QList<User> tmpFriendsList;
448 foreach (QVariant friendsVariant, result["friends"].toList()) {
449 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
450 QVariant distance = friendMap["distance"];
451 QMap<QString, QVariant> distanceMap = distance.toMap();
453 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
455 QUrl imageUrl = friendMap["profile_pic"].toUrl();
457 QString address = friendMap["address"].toString();
458 if(address.isEmpty()) {
459 QStringList location;
460 location.append(QString::number(coordinates.latitude()));
461 location.append(QString::number(coordinates.longitude()));
462 address = location.join(", ");
465 User buddy = User(address, coordinates, friendMap["name"].toString(),
466 friendMap["note"].toString(), imageUrl,
467 friendMap["timestamp"].toString(),
468 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
469 distanceMap["value"].toDouble());
471 if(imageUrl.isEmpty()) {
472 // friend doesn't have profile image, so we need to get him a silhouette image
473 m_defaultImage = true;
474 buddy.setProfileImage(AvatarImage::create(
475 QPixmap(":/res/images/empty_avatar.png"), AvatarImage::Small));
478 tmpFriendsList.append(buddy);
481 QHash<QString, QUrl> imageUrlList; // url list for images
483 // set unchanged profile images or add new images to imageUrlList for downloading
485 if(m_user->profileImageUrl() != user.profileImageUrl()) {
486 if(!user.profileImageUrl().isEmpty())
487 imageUrlList.insert(user.userId(), user.profileImageUrl());
489 user.setProfileImage(m_user->profileImage());
492 if(!user.profileImageUrl().isEmpty())
493 imageUrlList.insert(user.userId(), user.profileImageUrl());
496 // clear old user object
502 // create new user object from temporary user object
503 m_user = new User(user);
505 // set unchanged profile images or add new images to imageUrlList for downloading
506 if(!m_friendsList.isEmpty()) {
507 foreach(User tmpBuddy, tmpFriendsList) {
508 if(!tmpBuddy.profileImageUrl().isEmpty()) {
510 foreach(User *buddy, m_friendsList) {
511 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
512 tmpBuddy.setProfileImage(buddy->profileImage());
517 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
518 imageUrlList.insert(tmpBuddy.userId(), tmpBuddy.profileImageUrl());
522 foreach(User buddy, tmpFriendsList) {
523 if(!buddy.profileImageUrl().isEmpty())
524 imageUrlList.insert(buddy.userId(), buddy.profileImageUrl());
528 // clear old friendlist
529 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
530 m_friendsList.clear();
532 // populate new friendlist with temporary friendlist's data
533 foreach(User tmpFriendItem, tmpFriendsList) {
534 User *friendItem = new User(tmpFriendItem);
535 m_friendsList.append(friendItem);
537 tmpFriendsList.clear();
539 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
540 m_user->setTags(getTags(m_user->userId()));
542 emit userDataChanged(m_user, m_friendsList);
544 // set silhouette image to imageUrlList for downloading
546 imageUrlList.insert("", QUrl(SILHOUETTE_URL));
548 addProfileImages(imageUrlList);
549 imageUrlList.clear();
552 QVariant address = result.value("address");
553 if(!address.toString().isEmpty()) {
554 emit reverseGeoReady(address.toString());
556 QStringList coordinates;
557 coordinates.append(result.value("lat").toString());
558 coordinates.append(result.value("lon").toString());
560 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
561 emit reverseGeoReady(coordinates.join(", "));
567 void SituareService::imageReceived(const QString &id, const QPixmap &image)
569 qDebug() << __PRETTY_FUNCTION__;
571 if (m_user->userId() == id)
572 emit imageReady(id, AvatarImage::create(image, AvatarImage::Large));
574 emit imageReady(id, AvatarImage::create(image, AvatarImage::Small));
577 void SituareService::addProfileImages(const QHash<QString, QUrl> &imageUrlList)
579 qDebug() << __PRETTY_FUNCTION__;
581 QHashIterator<QString, QUrl> imageUrlListIterator(imageUrlList);
583 while (imageUrlListIterator.hasNext()) {
584 imageUrlListIterator.next();
585 emit fetchImage(imageUrlListIterator.key(), imageUrlListIterator.value());
589 void SituareService::clearUserData()
591 qDebug() << __PRETTY_FUNCTION__;
593 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
594 m_friendsList.clear();
600 emit userDataChanged(m_user, m_friendsList);
603 QHash<QString, QString> SituareService::getTags(const QString &userId)
605 qDebug() << __PRETTY_FUNCTION__;
607 return m_database->getTags(userId.toInt());
610 void SituareService::removeTags(const QStringList &tags)
612 qDebug() << __PRETTY_FUNCTION__;
614 m_database->removeTags(613374451, tags);
617 void SituareService::addTags(const QStringList &tags)
619 qDebug() << __PRETTY_FUNCTION__;
621 foreach (QString tag, tags)
622 m_database->addTag(613374451, tag);