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>
32 #include "network/networkaccessmanager.h"
33 #include "situarecommon.h"
34 #include "ui/avatarimage.h"
36 #include "situareservice.h"
38 SituareService::SituareService(QObject *parent)
42 qDebug() << __PRETTY_FUNCTION__;
44 m_networkManager = new NetworkAccessManager(this);
45 connect(m_networkManager, SIGNAL(finished(QNetworkReply*)),
46 this, SLOT(requestFinished(QNetworkReply*)), Qt::QueuedConnection);
48 m_imageFetcher = new ImageFetcher(new NetworkAccessManager(this), this);
49 connect(this, SIGNAL(fetchImage(QUrl)),
50 m_imageFetcher, SLOT(fetchImage(QUrl)));
51 connect(m_imageFetcher, SIGNAL(imageReceived(QUrl,QPixmap)),
52 this, SLOT(imageReceived(QUrl, QPixmap)));
53 connect(m_imageFetcher, SIGNAL(error(int, int)),
54 this, SIGNAL(error(int, int)));
57 SituareService::~SituareService()
59 qDebug() << __PRETTY_FUNCTION__;
66 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
67 m_friendsList.clear();
70 void SituareService::addProfileImages(const QList<QUrl> &imageUrlList)
72 qDebug() << __PRETTY_FUNCTION__;
74 foreach(QUrl url, imageUrlList) {
79 void SituareService::appendAccessToken(QString &requestUrl)
81 qWarning() << __PRETTY_FUNCTION__;
83 // requestUrl.append("access_token=");
84 requestUrl.append(m_session);
86 // qWarning() << __PRETTY_FUNCTION__ << "request url with parameters and access token:" << requestUrl;
89 void SituareService::buildRequest(const QString &script, const QHash<QString, QString> ¶meters)
91 qWarning() << __PRETTY_FUNCTION__;
93 QString url = SITUARE_URL;
98 if (!parameters.isEmpty()) {
99 QHash<QString, QString>::const_iterator i = parameters.constBegin();
100 while (i != parameters.constEnd()) {
103 url.append(i.value());
109 // qWarning() << __PRETTY_FUNCTION__ << "request url with parameters:" << url;
111 if (!m_session.isEmpty()) {
112 appendAccessToken(url);
115 m_requestsWaitingAccessToken.append(url);
116 ///< @todo emit login request
120 void SituareService::clearUserData()
122 qDebug() << __PRETTY_FUNCTION__;
124 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
125 m_friendsList.clear();
131 emit userDataChanged(m_user, m_friendsList);
134 QString SituareService::degreesToString(double degrees)
136 qDebug() << __PRETTY_FUNCTION__;
138 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
139 // additional digit is added for maximum precision
140 const int PRECISION = 10;
142 return QString::number(degrees, 'f', PRECISION);
145 void SituareService::fetchLocations()
147 qDebug() << __PRETTY_FUNCTION__;
149 QHash<QString, QString> parameters;
150 parameters.insert("extra_user_data", NORMAL_SIZE_PROFILE_IMAGE);
152 buildRequest(GET_LOCATIONS, parameters);
155 QString SituareService::formCookie(const QString &apiKeyValue, QString expiresValue,
156 QString userValue, QString sessionKeyValue,
157 QString sessionSecretValue, const QString &signatureValue,
158 const QString &localeValue)
160 qDebug() << __PRETTY_FUNCTION__;
167 QString sessionSecret;
170 QString signature = EQUAL_MARK;
171 QStringList variableList;
173 signature.append(signatureValue);
174 apiKey.append(apiKeyValue);
175 apiKey.append(UNDERLINE_MARK);
178 user.append(EQUAL_MARK);
179 expires.append(EXPIRES);
180 expires.append(EQUAL_MARK);
181 sessionKey.append(SESSION_KEY);
182 sessionKey.append(EQUAL_MARK);
183 sessionSecret.append(SESSION_SECRET);
184 sessionSecret.append(EQUAL_MARK);
185 locale.append(LOCALE);
186 locale.append(EQUAL_MARK);
187 locale.append(localeValue);
189 variableList.append(expires.append(expiresValue.append(BREAK_MARK)));
190 variableList.append(sessionKey.append(sessionKeyValue.append(BREAK_MARK)));
191 variableList.append(user.append(userValue).append(BREAK_MARK));
192 variableList.append(sessionSecret.append(sessionSecretValue.append(BREAK_MARK)));
194 cookie.append(BREAK_MARK);
196 foreach(variable, variableList) {
197 cookie.append(apiKey);
198 cookie.append(variable);
200 apiKey.remove(UNDERLINE_MARK);
201 cookie.append(apiKey);
202 cookie.append(signature);
203 cookie.append(BREAK_MARK);
204 cookie.append(locale);
211 QUrl SituareService::formUrl(const QString &baseUrl, const QString &phpScript,
212 QString urlParameters)
214 qDebug() << __PRETTY_FUNCTION__;
217 urlString.append(baseUrl);
218 urlString.append(phpScript);
219 if(!urlParameters.isEmpty())
220 urlString.append(urlParameters);
222 QUrl url = QUrl(urlString);
229 QString SituareService::formUrlParameters(const GeoCoordinate &coordinates, QString status,
232 qDebug() << __PRETTY_FUNCTION__;
234 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
235 // additional digit is added for maximum precision
236 const int COORDINATE_PRECISION = 10;
240 parameters.append(QUESTION_MARK);
241 parameters.append(LATITUDE);
242 parameters.append(EQUAL_MARK);
243 parameters.append(QString::number(coordinates.latitude(), 'f', COORDINATE_PRECISION));
244 parameters.append(AMBERSAND_MARK);
245 parameters.append(LONGTITUDE);
246 parameters.append(EQUAL_MARK);
247 parameters.append(QString::number(coordinates.longitude(), 'f', COORDINATE_PRECISION));
249 parameters.append(AMBERSAND_MARK);
250 parameters.append(PUBLISH);
251 parameters.append(EQUAL_MARK);
254 parameters.append(PUBLISH_TRUE);
256 parameters.append(PUBLISH_FALSE);
258 if(!status.isEmpty()) {
259 parameters.append(AMBERSAND_MARK);
260 parameters.append(DATA);
261 parameters.append(EQUAL_MARK);
262 parameters.append(status);
268 void SituareService::imageReceived(const QUrl &url, const QPixmap &image)
270 qDebug() << __PRETTY_FUNCTION__;
271 qDebug() << "Image URL: " << url << " size :" << image.size();
273 // assign facebook silhouette image to all who doesn't have a profile image
274 if(url == QUrl(SILHOUETTE_URL)) {
275 if(m_user->profileImageUrl().isEmpty()) {
276 m_user->setProfileImage(AvatarImage::create(image, AvatarImage::Large));
277 emit imageReady(m_user);
279 foreach(User *friendItem, m_friendsList) {
280 if(friendItem->profileImageUrl().isEmpty()) {
281 friendItem->setProfileImage(AvatarImage::create(image, AvatarImage::Small));
282 emit imageReady(friendItem);
287 if (m_user->profileImageUrl() == url) {
288 m_user->setProfileImage(AvatarImage::create(image, AvatarImage::Large));
289 emit imageReady(m_user);
292 foreach(User *friendItem, m_friendsList) {
293 if(friendItem->profileImageUrl() == url) {
294 friendItem->setProfileImage(AvatarImage::create(image, AvatarImage::Small));
295 emit imageReady(friendItem);
300 void SituareService::parseUserData(const QByteArray &jsonReply)
302 qDebug() << __PRETTY_FUNCTION__;
304 m_defaultImage = false;
306 QJson::Parser parser;
309 QVariantMap result = parser.parse (jsonReply, &ok).toMap();
311 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
315 if(result.contains("ErrorCode")) {
316 QVariant errorVariant = result.value("ErrorCode");
317 emit error(ErrorContext::SITUARE, errorVariant.toInt());
319 } else if(result.contains("user")) {
321 QVariant userVariant = result.value("user");
322 QMap<QString, QVariant> userMap = userVariant.toMap();
324 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
326 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
328 if(imageUrl.isEmpty()) {
329 // user doesn't have profile image, so we need to get him a silhouette image
330 m_defaultImage = true;
333 QString address = userMap["address"].toString();
334 if(address.isEmpty()) {
335 QStringList location;
336 location.append(QString::number(coordinates.latitude()));
337 location.append(QString::number(coordinates.longitude()));
338 address = location.join(", ");
341 User user = User(address, coordinates, userMap["name"].toString(),
342 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
343 true, userMap["uid"].toString());
345 QList<User> tmpFriendsList;
347 foreach (QVariant friendsVariant, result["friends"].toList()) {
348 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
349 QVariant distance = friendMap["distance"];
350 QMap<QString, QVariant> distanceMap = distance.toMap();
352 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
354 QUrl imageUrl = friendMap["profile_pic"].toUrl();
356 if(imageUrl.isEmpty()) {
357 // friend doesn't have profile image, so we need to get him a silhouette image
358 m_defaultImage = true;
361 QString address = friendMap["address"].toString();
362 if(address.isEmpty()) {
363 QStringList location;
364 location.append(QString::number(coordinates.latitude()));
365 location.append(QString::number(coordinates.longitude()));
366 address = location.join(", ");
369 User buddy = User(address, coordinates, friendMap["name"].toString(),
370 friendMap["note"].toString(), imageUrl,
371 friendMap["timestamp"].toString(),
372 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
373 distanceMap["value"].toDouble());
375 tmpFriendsList.append(buddy);
378 QList<QUrl> imageUrlList; // url list for images
380 // set unchanged profile images or add new images to imageUrlList for downloading
382 if(m_user->profileImageUrl() != user.profileImageUrl()) {
383 if(!user.profileImageUrl().isEmpty())
384 imageUrlList.append(user.profileImageUrl());
386 user.setProfileImage(m_user->profileImage());
389 if(!user.profileImageUrl().isEmpty())
390 imageUrlList.append(user.profileImageUrl());
393 // clear old user object
399 // create new user object from temporary user object
400 m_user = new User(user);
402 // set unchanged profile images or add new images to imageUrlList for downloading
403 if(!m_friendsList.isEmpty()) {
404 foreach(User tmpBuddy, tmpFriendsList) {
405 if(!tmpBuddy.profileImageUrl().isEmpty()) {
407 foreach(User *buddy, m_friendsList) {
408 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
409 tmpBuddy.setProfileImage(buddy->profileImage());
414 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
415 imageUrlList.append(tmpBuddy.profileImageUrl());
419 foreach(User buddy, tmpFriendsList) {
420 if(!buddy.profileImageUrl().isEmpty())
421 imageUrlList.append(buddy.profileImageUrl());
425 // clear old friendlist
426 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
427 m_friendsList.clear();
429 // populate new friendlist with temporary friendlist's data
430 foreach(User tmpFriendItem, tmpFriendsList) {
431 User *friendItem = new User(tmpFriendItem);
432 m_friendsList.append(friendItem);
434 tmpFriendsList.clear();
436 emit userDataChanged(m_user, m_friendsList);
438 // set silhouette image to imageUrlList for downloading
440 imageUrlList.append(QUrl(SILHOUETTE_URL));
442 addProfileImages(imageUrlList);
443 imageUrlList.clear();
445 QVariant address = result.value("address");
446 if(!address.toString().isEmpty()) {
447 emit reverseGeoReady(address.toString());
449 QStringList coordinates;
450 coordinates.append(result.value("lat").toString());
451 coordinates.append(result.value("lon").toString());
453 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
454 emit reverseGeoReady(coordinates.join(", "));
460 void SituareService::requestFinished(QNetworkReply *reply)
462 qDebug() << __PRETTY_FUNCTION__;
465 if (m_currentRequests.contains(reply)) {
467 qDebug() << "BytesAvailable: " << reply->bytesAvailable();
469 if (reply->error()) {
470 emit error(ErrorContext::NETWORK, reply->error());
472 QByteArray replyArray = reply->readAll();
473 qDebug() << "Reply from: " << reply->url() << "reply " << replyArray;
475 if(replyArray == ERROR_LAT.toAscii()) {
476 qDebug() << "Error: " << ERROR_LAT;
477 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
478 } else if(replyArray == ERROR_LON.toAscii()) {
479 qDebug() << "Error: " << ERROR_LON;
480 emit error(ErrorContext::SITUARE, SituareError::UPDATE_FAILED);
481 } else if(replyArray.contains(ERROR_SESSION.toAscii())) {
482 qDebug() << "Error: " << ERROR_SESSION;
483 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
484 } else if(replyArray.startsWith(OPENING_BRACE_MARK.toAscii())) {
485 qDebug() << "JSON string";
486 parseUserData(replyArray);
487 } else if(replyArray.isEmpty()) {
488 if(reply->url().toString().contains(UPDATE_LOCATION.toAscii())) {
489 emit updateWasSuccessful();
491 // session credentials are invalid
492 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
496 emit error(ErrorContext::SITUARE, SituareError::ERROR_GENERAL);
499 m_currentRequests.removeAll(reply);
500 reply->deleteLater();
504 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
506 qDebug() << __PRETTY_FUNCTION__;
508 QHash<QString, QString> parameters;
509 parameters.insert("lat", degreesToString(coordinates.latitude()));
510 parameters.insert("lon", degreesToString(coordinates.longitude()));
511 parameters.insert("format", "json");
513 buildRequest(REVERSE_GEO, parameters);
516 void SituareService::sendRequest(const QUrl &url, const QString &cookieType, const QString &cookie)
518 qDebug() << __PRETTY_FUNCTION__;
520 QNetworkRequest request;
523 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
524 request.setRawHeader(cookieType.toAscii(), cookie.toUtf8());
526 QNetworkReply *reply = m_networkManager->get(request, true);
528 m_currentRequests.append(reply);
531 void SituareService::sendRequest(const QString &requestUrl)
533 qWarning() << __PRETTY_FUNCTION__ << requestUrl;
535 // make and send the request
536 QNetworkRequest request;
537 request.setUrl(QUrl(requestUrl));
538 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
539 QNetworkReply *reply = m_networkManager->get(request, true);
540 m_currentRequests.append(reply);
543 void SituareService::updateSession(const QString &session)
545 qWarning() << __PRETTY_FUNCTION__;
549 foreach (QString request, m_requestsWaitingAccessToken) {
550 appendAccessToken(request);
551 sendRequest(request);
554 m_requestsWaitingAccessToken.clear();
557 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
560 qDebug() << __PRETTY_FUNCTION__;
562 QHash<QString, QString> parameters;
563 parameters.insert("lat", degreesToString(coordinates.latitude()));
564 parameters.insert("lon", degreesToString(coordinates.longitude()));
565 parameters.insert("publish", publish ? "true" : "false");
566 parameters.insert("data", status); ///< @todo if !empty ???
568 buildRequest(UPDATE_LOCATION, parameters);