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 <QNetworkReply>
28 #include <QStringList>
29 #include <QtAlgorithms>
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)));
58 SituareService::~SituareService()
60 qDebug() << __PRETTY_FUNCTION__;
67 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
68 m_friendsList.clear();
71 void SituareService::addProfileImages(const QList<QUrl> &imageUrlList)
73 qDebug() << __PRETTY_FUNCTION__;
75 foreach(QUrl url, imageUrlList) {
80 void SituareService::appendAccessToken(QString &requestUrl)
82 qDebug() << __PRETTY_FUNCTION__;
84 requestUrl.append(m_session);
87 void SituareService::buildRequest(const QString &script, const QHash<QString, QString> ¶meters)
89 qDebug() << __PRETTY_FUNCTION__;
91 const QString PARAMETER_KEY_API = "api";
92 const QString PARAMETER_VALUE_API = "2.0";
94 QString url = SITUARE_URL;
98 // append default api version parameter if not yet specified
99 if (!parameters.contains(PARAMETER_KEY_API))
100 url.append(PARAMETER_KEY_API + "=" + PARAMETER_VALUE_API + "&");
103 if (!parameters.isEmpty()) {
104 QHash<QString, QString>::const_iterator i = parameters.constBegin();
105 while (i != parameters.constEnd()) {
108 url.append(i.value());
114 /// @todo BUG: Url parameter strings are not url escaped
116 // qWarning() << __PRETTY_FUNCTION__ << "request url with parameters:" << url;
118 if (!m_session.isEmpty()) {
119 appendAccessToken(url);
122 emit error(ErrorContext::SITUARE, SituareError::SESSION_EXPIRED);
126 void SituareService::clearUserData()
128 qDebug() << __PRETTY_FUNCTION__;
130 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
131 m_friendsList.clear();
137 emit userDataChanged(m_user, m_friendsList);
140 QString SituareService::degreesToString(double degrees)
142 qDebug() << __PRETTY_FUNCTION__;
144 // one scene pixel is about 5.4e-6 degrees, the integer part is max three digits and one
145 // additional digit is added for maximum precision
146 const int PRECISION = 10;
148 return QString::number(degrees, 'f', PRECISION);
151 void SituareService::fetchLocations()
153 qDebug() << __PRETTY_FUNCTION__;
155 QHash<QString, QString> parameters;
156 parameters.insert("extra_user_data", NORMAL_SIZE_PROFILE_IMAGE);
158 buildRequest(GET_LOCATIONS, parameters);
161 SituareService::RequestName SituareService::getRequestName(const QUrl &url) const
163 qDebug() << __PRETTY_FUNCTION__;
165 if (url.toString().contains(GET_LOCATIONS))
166 return SituareService::RequestGetLocations;
167 else if (url.toString().contains(UPDATE_LOCATION))
168 return SituareService::RequestUpdateLocation;
169 else if (url.toString().contains(REVERSE_GEO))
170 return SituareService::RequestReverseGeo;
172 return SituareService::RequestUnknown;
175 void SituareService::imageReceived(const QUrl &url, const QPixmap &image)
177 qDebug() << __PRETTY_FUNCTION__;
178 qDebug() << "Image URL: " << url << " size :" << image.size();
180 // assign facebook silhouette image to all who doesn't have a profile image
181 if(url == QUrl(SILHOUETTE_URL)) {
182 if(m_user->profileImageUrl().isEmpty()) {
183 m_user->setProfileImage(AvatarImage::create(image, AvatarImage::Large));
184 emit imageReady(m_user);
186 foreach(User *friendItem, m_friendsList) {
187 if(friendItem->profileImageUrl().isEmpty()) {
188 friendItem->setProfileImage(AvatarImage::create(image, AvatarImage::Small));
189 emit imageReady(friendItem);
194 if (m_user->profileImageUrl() == url) {
195 m_user->setProfileImage(AvatarImage::create(image, AvatarImage::Large));
196 emit imageReady(m_user);
199 foreach(User *friendItem, m_friendsList) {
200 if(friendItem->profileImageUrl() == url) {
201 friendItem->setProfileImage(AvatarImage::create(image, AvatarImage::Small));
202 emit imageReady(friendItem);
207 void SituareService::parseReply(const QByteArray &jsonReply, RequestName requestName)
209 qDebug() << __PRETTY_FUNCTION__;
211 QJson::Parser parser;
214 QVariantMap result = parser.parse(jsonReply, &ok).toMap();
217 emit error(ErrorContext::SITUARE, SituareError::INVALID_JSON);
219 QVariant resultStatus = result["ResultStatus"];
220 QVariant resultData = result["ResultData"];
222 if (resultStatus.toString() == "ERROR") {
223 QVariantMap errorData = resultData.toMap();
224 emit error(ErrorContext::SITUARE, errorData["ErrorCode"].toInt());
225 } else if (resultStatus.toString() == "OK") {
226 if (requestName == SituareService::RequestGetLocations)
227 parseUserData(resultData);
228 else if (requestName == SituareService::RequestUpdateLocation)
229 emit updateWasSuccessful();
230 else if (requestName == SituareService::RequestReverseGeo)
231 parseReverseGeoData(resultData);
236 void SituareService::parseReverseGeoData(const QVariant &reverseGeoData)
238 qDebug() << __PRETTY_FUNCTION__;
240 QVariantMap result = reverseGeoData.toMap();
242 if (result.contains("address") && !result["address"].toString().isEmpty()) {
243 emit reverseGeoReady(result["address"].toString());
245 QStringList coordinates;
246 coordinates.append(result["lat"].toString());
247 coordinates.append(result["lon"].toString());
248 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
249 emit reverseGeoReady(coordinates.join(", "));
253 void SituareService::parseUserData(const QVariant &userData)
255 qDebug() << __PRETTY_FUNCTION__;
257 m_defaultImage = false;
259 QVariantMap result = userData.toMap();
261 if (result.contains("user")) {
263 QVariant userVariant = result.value("user");
264 QMap<QString, QVariant> userMap = userVariant.toMap();
266 GeoCoordinate coordinates(userMap["latitude"].toReal(), userMap["longitude"].toReal());
268 QUrl imageUrl = userMap[NORMAL_SIZE_PROFILE_IMAGE].toUrl();
270 if(imageUrl.isEmpty()) {
271 // user doesn't have profile image, so we need to get him a silhouette image
272 m_defaultImage = true;
275 QString address = userMap["address"].toString();
276 if(address.isEmpty()) {
277 QStringList location;
278 location.append(QString::number(coordinates.latitude()));
279 location.append(QString::number(coordinates.longitude()));
280 address = location.join(", ");
283 User user = User(address, coordinates, userMap["name"].toString(),
284 userMap["note"].toString(), imageUrl, userMap["timestamp"].toString(),
285 true, userMap["uid"].toString());
287 QList<User> tmpFriendsList;
289 foreach (QVariant friendsVariant, result["friends"].toList()) {
290 QMap<QString, QVariant> friendMap = friendsVariant.toMap();
291 QVariant distance = friendMap["distance"];
292 QMap<QString, QVariant> distanceMap = distance.toMap();
294 GeoCoordinate coordinates(friendMap["latitude"].toReal(),friendMap["longitude"].toReal());
296 QUrl imageUrl = friendMap["profile_pic"].toUrl();
298 if(imageUrl.isEmpty()) {
299 // friend doesn't have profile image, so we need to get him a silhouette image
300 m_defaultImage = true;
303 QString address = friendMap["address"].toString();
304 if(address.isEmpty()) {
305 QStringList location;
306 location.append(QString::number(coordinates.latitude()));
307 location.append(QString::number(coordinates.longitude()));
308 address = location.join(", ");
311 User buddy = User(address, coordinates, friendMap["name"].toString(),
312 friendMap["note"].toString(), imageUrl,
313 friendMap["timestamp"].toString(),
314 false, friendMap["uid"].toString(), distanceMap["units"].toString(),
315 distanceMap["value"].toDouble());
317 tmpFriendsList.append(buddy);
320 QList<QUrl> imageUrlList; // url list for images
322 // set unchanged profile images or add new images to imageUrlList for downloading
324 if(m_user->profileImageUrl() != user.profileImageUrl()) {
325 if(!user.profileImageUrl().isEmpty())
326 imageUrlList.append(user.profileImageUrl());
328 user.setProfileImage(m_user->profileImage());
331 if(!user.profileImageUrl().isEmpty())
332 imageUrlList.append(user.profileImageUrl());
335 // clear old user object
341 // create new user object from temporary user object
342 m_user = new User(user);
344 // set unchanged profile images or add new images to imageUrlList for downloading
345 if(!m_friendsList.isEmpty()) {
346 foreach(User tmpBuddy, tmpFriendsList) {
347 if(!tmpBuddy.profileImageUrl().isEmpty()) {
349 foreach(User *buddy, m_friendsList) {
350 if(tmpBuddy.profileImageUrl() == buddy->profileImageUrl()) {
351 tmpBuddy.setProfileImage(buddy->profileImage());
356 if(!found && !tmpBuddy.profileImageUrl().isEmpty())
357 imageUrlList.append(tmpBuddy.profileImageUrl());
361 foreach(User buddy, tmpFriendsList) {
362 if(!buddy.profileImageUrl().isEmpty())
363 imageUrlList.append(buddy.profileImageUrl());
367 // clear old friendlist
368 qDeleteAll(m_friendsList.begin(), m_friendsList.end());
369 m_friendsList.clear();
371 // populate new friendlist with temporary friendlist's data
372 foreach(User tmpFriendItem, tmpFriendsList) {
373 User *friendItem = new User(tmpFriendItem);
374 m_friendsList.append(friendItem);
376 tmpFriendsList.clear();
378 emit userDataChanged(m_user, m_friendsList);
380 // set silhouette image to imageUrlList for downloading
382 imageUrlList.append(QUrl(SILHOUETTE_URL));
384 addProfileImages(imageUrlList);
385 imageUrlList.clear();
387 QVariant address = result.value("address");
388 if(!address.toString().isEmpty()) {
389 emit reverseGeoReady(address.toString());
391 QStringList coordinates;
392 coordinates.append(result.value("lat").toString());
393 coordinates.append(result.value("lon").toString());
395 emit error(ErrorContext::SITUARE, SituareError::ADDRESS_RETRIEVAL_FAILED);
396 emit reverseGeoReady(coordinates.join(", "));
401 void SituareService::requestFinished(QNetworkReply *reply)
403 qDebug() << __PRETTY_FUNCTION__;
406 if (m_currentRequests.contains(reply)) {
409 emit error(ErrorContext::NETWORK, reply->error());
411 parseReply(reply->readAll(), getRequestName(reply->url()));
413 m_currentRequests.removeAll(reply);
414 reply->deleteLater();
418 void SituareService::reverseGeo(const GeoCoordinate &coordinates)
420 qDebug() << __PRETTY_FUNCTION__;
422 QHash<QString, QString> parameters;
423 parameters.insert("lat", degreesToString(coordinates.latitude()));
424 parameters.insert("lon", degreesToString(coordinates.longitude()));
425 parameters.insert("format", "json");
427 buildRequest(REVERSE_GEO, parameters);
430 void SituareService::sendRequest(const QString &requestUrl)
432 qDebug() << __PRETTY_FUNCTION__ << "requestUrl" << requestUrl;
434 // make and send the request
435 QNetworkRequest request;
436 request.setUrl(QUrl(requestUrl));
437 request.setAttribute(QNetworkRequest::CacheSaveControlAttribute, false);
438 QNetworkReply *reply = m_networkManager->get(request, true);
439 m_currentRequests.append(reply);
442 void SituareService::updateSession(const QString &session)
444 qDebug() << __PRETTY_FUNCTION__;
448 if (m_session.isEmpty())
452 void SituareService::updateLocation(const GeoCoordinate &coordinates, const QString &status,
455 qDebug() << __PRETTY_FUNCTION__;
457 QHash<QString, QString> parameters;
458 parameters.insert("lat", degreesToString(coordinates.latitude()));
459 parameters.insert("lon", degreesToString(coordinates.longitude()));
460 parameters.insert("publish", publish ? "true" : "false");
461 parameters.insert("data", status); ///< @todo if !empty ???
463 buildRequest(UPDATE_LOCATION, parameters);