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 <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(QObject *parent)
43 qDebug() << __PRETTY_FUNCTION__;
45 m_networkManager = new NetworkAccessManager(this);
46 connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
47 this, SLOT(requestFinished(QNetworkReply*)), Qt::QueuedConnection);
49 m_imageFetcher = new ImageFetcher(new NetworkAccessManager(this), this);
50 connect(this, SIGNAL(fetchImage(QUrl)),
51 m_imageFetcher, SLOT(fetchImage(QUrl)));
52 connect(m_imageFetcher, SIGNAL(imageReceived(QUrl,QPixmap)),
53 this, SLOT(imageReceived(QUrl, QPixmap)));
54 connect(m_imageFetcher, SIGNAL(error(int, int)),
55 this, SIGNAL(error(int, int)));
57 m_database = new Database(this);
58 m_database->openDatabase();
59 m_database->createMessageTable();
60 m_database->createUserTable();
61 m_database->createTagTable();
62 m_database->createUserTagTable();
65 SituareService::~SituareService()
67 qDebug() << __PRETTY_FUNCTION__;
74 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
75 m_friendsList.clear();
78 void SituareService::fetchMessages()
80 qDebug() << __PRETTY_FUNCTION__;
82 // QList<Message> messages = m_database->getMessages(m_user->userId().toULongLong());
84 // foreach (User *user, m_friendsList) {
85 // foreach (Message message, messages)
86 // if (message.senderId() == user->userId())
87 // message.setSenderAvatarImage(user->profileImage());
90 // emit messagesReceived(messages);
93 // qWarning() << m_credentials.userID();
94 // qWarning() << m_credentials.sessionKey();
95 // qWarning() << m_credentials.sessionSecret();
98 void SituareService::fetchPeopleWithSimilarInterest(const GeoCoordinate &southWestCoordinates,
99 const GeoCoordinate &northEastCoordinates)
101 qDebug() << __PRETTY_FUNCTION__;
103 QList<User *> interestingPeople;
104 QStringList userIds = m_database->getInterestingPeople(m_user->userId().toULongLong(),
105 southWestCoordinates,
106 northEastCoordinates);
107 qWarning() << userIds;
108 foreach (User *user, m_friendsList) {
109 if (userIds.contains(user->userId()))
110 interestingPeople.append(user);
113 emit interestingPeopleReceived(interestingPeople);
116 void SituareService::fetchLocations()
118 qDebug() << __PRETTY_FUNCTION__;
120 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
121 m_credentials.sessionKey(), m_credentials.sessionSecret(),
122 m_credentials.sig(), EN_LOCALE);
124 QUrl url = formUrl(SITUARE_URL, GET_LOCATIONS);
125 sendRequest(url, COOKIE, cookie);
128 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
130 qDebug() << __PRETTY_FUNCTION__;
132 QString cookie = formCookie(API_KEY, m_credentials.expires(),m_credentials.userID(),
133 m_credentials.sessionKey(), m_credentials.sessionSecret(),
134 m_credentials.sig(), EN_LOCALE);
136 QString urlParameters = formUrlParameters(coordinates);
137 urlParameters.append(JSON_FORMAT);
138 QUrl url = formUrl(SITUARE_URL, REVERSE_GEO, urlParameters);
140 sendRequest(url, COOKIE, cookie);
143 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
146 qDebug() << __PRETTY_FUNCTION__;
148 QString urlParameters = formUrlParameters(coordinates, status, publish);
149 QUrl url = formUrl(SITUARE_URL, UPDATE_LOCATION, urlParameters);
151 QString cookie = formCookie(API_KEY, m_credentials.expires(), m_credentials.userID(),
152 m_credentials.sessionKey(), m_credentials.sessionSecret(),
153 m_credentials.sig(), EN_LOCALE);
155 sendRequest(url, COOKIE, cookie);
158 QString SituareService::formCookie(const QString &apiKeyValue, QString expiresValue,
159 QString userValue, QString sessionKeyValue,
160 QString sessionSecretValue, const QString &signatureValue,
161 const QString &localeValue)
163 qDebug() << __PRETTY_FUNCTION__;
170 QString sessionSecret;
173 QString signature = EQUAL_MARK;
174 QStringList variableList;
176 signature.append(signatureValue);
177 apiKey.append(apiKeyValue);
178 apiKey.append(UNDERLINE_MARK);
181 user.append(EQUAL_MARK);
182 expires.append(EXPIRES);
183 expires.append(EQUAL_MARK);
184 sessionKey.append(SESSION_KEY);
185 sessionKey.append(EQUAL_MARK);
186 sessionSecret.append(SESSION_SECRET);
187 sessionSecret.append(EQUAL_MARK);
188 locale.append(LOCALE);
189 locale.append(EQUAL_MARK);
190 locale.append(localeValue);
192 variableList.append(expires.append(expiresValue.append(BREAK_MARK)));
193 variableList.append(sessionKey.append(sessionKeyValue.append(BREAK_MARK)));
194 variableList.append(user.append(userValue).append(BREAK_MARK));
195 variableList.append(sessionSecret.append(sessionSecretValue.append(BREAK_MARK)));
197 cookie.append(BREAK_MARK);
199 foreach(variable, variableList) {
200 cookie.append(apiKey);
201 cookie.append(variable);
203 apiKey.remove(UNDERLINE_MARK);
204 cookie.append(apiKey);
205 cookie.append(signature);
206 cookie.append(BREAK_MARK);
207 cookie.append(locale);
209 qWarning() << cookie;
214 QUrl SituareService::formUrl(const QString &baseUrl, const QString &phpScript,
215 QString urlParameters)
217 qDebug() << __PRETTY_FUNCTION__;
220 urlString.append(baseUrl);
221 urlString.append(phpScript);
222 if(!urlParameters.isEmpty())
223 urlString.append(urlParameters);
225 QUrl url = QUrl(urlString);
232 QString SituareService::formUrlParameters(const GeoCoordinate &coordinates, QString status,
235 qDebug() << __PRETTY_FUNCTION__;
237 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
238 // additional digit is added for maximum precision
239 const int COORDINATE_PRECISION = 10;
243 parameters.append(QUESTION_MARK);
244 parameters.append(LATITUDE);
245 parameters.append(EQUAL_MARK);
246 parameters.append(QString::number(coordinates.latitude(), 'f', COORDINATE_PRECISION));
247 parameters.append(AMBERSAND_MARK);
248 parameters.append(LONGTITUDE);
249 parameters.append(EQUAL_MARK);
250 parameters.append(QString::number(coordinates.longitude(), 'f', COORDINATE_PRECISION));
252 parameters.append(AMBERSAND_MARK);
253 parameters.append(PUBLISH);
254 parameters.append(EQUAL_MARK);
257 parameters.append(PUBLISH_TRUE);
259 parameters.append(PUBLISH_FALSE);
261 if(!status.isEmpty()) {
262 parameters.append(AMBERSAND_MARK);
263 parameters.append(DATA);
264 parameters.append(EQUAL_MARK);
265 parameters.append(status);
271 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
273 qDebug() << __PRETTY_FUNCTION__;
275 QNetworkRequest request;
278 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
279 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
281 QNetworkReply *reply = m_networkManager->get(request, true);
283 m_currentRequests.append(reply);
286 void SituareService::requestFinished(QNetworkReply *reply)
288 qDebug() << __PRETTY_FUNCTION__;
291 if (m_currentRequests.contains(reply)) {
293 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
295 if (reply->error()) {
296 emit error(ErrorContext::NETWORK, reply->error());
298 QByteArray replyArray = reply->readAll();
299 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
301 if(replyArray == ERROR_LAT.toAscii()) {
302 qDebug() << "Error: " << ERROR_LAT;
303 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
304 } else if(replyArray == ERROR_LON.toAscii()) {
305 qDebug() << "Error: " << ERROR_LON;
306 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
307 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
308 qDebug() << "Error: " << ERROR_SESSION;
309 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
310 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
311 qDebug() << "JSON string";
312 parseUserData(replyArray);
313 } else if(replyArray.isEmpty()) {
314 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
315 emit updateWasSuccessful();
317 // session credentials are invalid
318 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
322 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
325 m_currentRequests.removeAll(reply);
326 reply->deleteLater();
330 void SituareService::credentialsReady(const FacebookCredentials &credentials)
332 qDebug() << __PRETTY_FUNCTION__;
334 m_credentials = credentials;
337 void SituareService::parseUserData(const QByteArray &jsonReply)
339 qDebug() << __PRETTY_FUNCTION__;
341 m_defaultImage = false;
343 QJson::Parser parser;
346 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
348 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
352 if(result.contains("ErrorCode")) {
353 QVariant errorVariant = result.value("ErrorCode");
354 emit error(ErrorContext::SITUARE, errorVariant.toInt());
356 } else if(result.contains("user")) {
358 QVariant userVariant = result.value("user");
359 QMap<QString, QVariant> userMap = userVariant.toMap();
361 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
363 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
365 if(imageUrl.isEmpty()) {
366 // user doesn't have profile image, so we need to get him a silhouette image
367 m_defaultImage = true;
370 QString address = userMap["address"].toString();
371 if(address.isEmpty()) {
372 QStringList location;
373 location.append(QString::number(coordinates.latitude()));
374 location.append(QString::number(coordinates.longitude()));
375 address = location.join(", ");
378 User user = User(address, coordinates, userMap["name"].toString(),
379 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
380 true, userMap["uid"].toString());
382 QList<User> tmpFriendsList;
384 foreach (QVariant friendsVariant, result["friends"].toList()) {
385 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
386 QVariant distance = friendMap["distance"];
387 QMap<QString, QVariant> distanceMap = distance.toMap();
389 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
391 QUrl imageUrl = friendMap["profile_pic"].toUrl();
393 if(imageUrl.isEmpty()) {
394 // friend doesn't have profile image, so we need to get him a silhouette image
395 m_defaultImage = true;
398 QString address = friendMap["address"].toString();
399 if(address.isEmpty()) {
400 QStringList location;
401 location.append(QString::number(coordinates.latitude()));
402 location.append(QString::number(coordinates.longitude()));
403 address = location.join(", ");
406 User buddy = User(address, coordinates, friendMap["name"].toString(),
407 friendMap["note"].toString(), imageUrl,
408 friendMap["timestamp"].toString(),
409 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
410 distanceMap["value"].toDouble());
412 tmpFriendsList.append(buddy);
415 QList<QUrl> imageUrlList; // url list for images
417 // set unchanged profile images or add new images to imageUrlList for downloading
419 if(m_user->profileImageUrl() != user.profileImageUrl()) {
420 if(!user.profileImageUrl().isEmpty())
421 imageUrlList.append(user.profileImageUrl());
423 user.setProfileImage(m_user->profileImage());
426 if(!user.profileImageUrl().isEmpty())
427 imageUrlList.append(user.profileImageUrl());
430 // clear old user object
436 // create new user object from temporary user object
437 m_user = new User(user);
439 // set unchanged profile images or add new images to imageUrlList for downloading
440 if(!m_friendsList.isEmpty()) {
441 foreach(User tmpBuddy, tmpFriendsList) {
442 if(!tmpBuddy.profileImageUrl().isEmpty()) {
444 foreach(User *buddy, m_friendsList) {
445 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
446 tmpBuddy.setProfileImage(buddy->profileImage());
451 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
452 imageUrlList.append(tmpBuddy.profileImageUrl());
456 foreach(User buddy, tmpFriendsList) {
457 if(!buddy.profileImageUrl().isEmpty())
458 imageUrlList.append(buddy.profileImageUrl());
462 // clear old friendlist
463 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
464 m_friendsList.clear();
466 // populate new friendlist with temporary friendlist's data
467 foreach(User tmpFriendItem, tmpFriendsList) {
468 User *friendItem = new User(tmpFriendItem);
469 m_friendsList.append(friendItem);
471 tmpFriendsList.clear();
473 //REMOVE WHEN NOT NEEDED! get user tags and set tags to the user
474 m_user->setTags(getTags(m_user->userId()));
476 emit userDataChanged(m_user, m_friendsList);
478 // set silhouette image to imageUrlList for downloading
480 imageUrlList.append(QUrl(SILHOUETTE_URL));
482 addProfileImages(imageUrlList);
483 imageUrlList.clear();
485 //REMOVE WHEN NOT NEEDED! fetch messages
489 QVariant address = result.value("address");
490 if(!address.toString().isEmpty()) {
491 emit reverseGeoReady(address.toString());
493 QStringList coordinates;
494 coordinates.append(result.value("lat").toString());
495 coordinates.append(result.value("lon").toString());
497 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
498 emit reverseGeoReady(coordinates.join(", "));
504 void SituareService::imageReceived(const QUrl &url, const QPixmap &image)
506 qDebug() << __PRETTY_FUNCTION__;
507 qDebug() << "Image URL: " << url << " size :" << image.size();
509 // assign facebook silhouette image to all who doesn't have a profile image
510 if(url == QUrl(SILHOUETTE_URL)) {
511 if(m_user->profileImageUrl().isEmpty()) {
512 m_user->setProfileImage(AvatarImage::create(image, AvatarImage::Large));
513 emit imageReady(m_user);
515 foreach(User *friendItem, m_friendsList) {
516 if(friendItem->profileImageUrl().isEmpty()) {
517 friendItem->setProfileImage(AvatarImage::create(image, AvatarImage::Small));
518 emit imageReady(friendItem);
523 if (m_user->profileImageUrl() == url) {
524 m_user->setProfileImage(AvatarImage::create(image, AvatarImage::Large));
525 emit imageReady(m_user);
528 foreach(User *friendItem, m_friendsList) {
529 if(friendItem->profileImageUrl() == url) {
530 friendItem->setProfileImage(AvatarImage::create(image, AvatarImage::Small));
531 emit imageReady(friendItem);
536 void SituareService::addProfileImages(const QList<QUrl> &imageUrlList)
538 qDebug() << __PRETTY_FUNCTION__;
540 foreach(QUrl url, imageUrlList) {
541 emit fetchImage(url);
545 void SituareService::clearUserData()
547 qDebug() << __PRETTY_FUNCTION__;
549 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
550 m_friendsList.clear();
556 emit userDataChanged(m_user, m_friendsList);
559 QStringList SituareService::getTags(const QString &userId)
561 qDebug() << __PRETTY_FUNCTION__;
563 return m_database->getTags(userId.toInt());
566 void SituareService::updateTags(const QString &userId, const QStringList &tags)
568 qDebug() << __PRETTY_FUNCTION__;
570 foreach (QString tag, tags)
571 m_database->addTag(userId.toInt(), tag);