Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qplacemanagerengine_nokiav2.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
5
6#include "placesv2/qplacecategoriesreplyhere.h"
7#include "placesv2/qplacecontentreplyimpl.h"
8#include "placesv2/qplacesearchsuggestionreplyimpl.h"
9#include "placesv2/qplacesearchreplyhere.h"
10#include "placesv2/qplacedetailsreplyimpl.h"
13#include "uri_constants.h"
15
16#include <QCoreApplication>
17#include <QtCore/QFile>
18#include <QtCore/QJsonArray>
19#include <QtCore/QJsonDocument>
20#include <QtCore/QJsonObject>
21#include <QtCore/QRegularExpression>
22#include <QtCore/QStandardPaths>
23#include <QtCore/QUrlQuery>
24#include <QtNetwork/QNetworkProxy>
25#include <QtNetwork/QNetworkProxyFactory>
26
27#include <QtLocation/QPlace>
28#include <QtLocation/QPlaceContentRequest>
29#include <QtLocation/QPlaceDetailsReply>
30#include <QtLocation/QPlaceIcon>
31#include <QtLocation/QPlaceSearchRequest>
32#include <QtPositioning/QGeoCircle>
33
35
36static const char FIXED_CATEGORIES_string[] =
37 "eat-drink\0"
38 "going-out\0"
39 "sights-museums\0"
40 "transport\0"
41 "accommodation\0"
42 "shopping\0"
43 "leisure-outdoor\0"
44 "administrative-areas-buildings\0"
45 "natural-geographical\0"
46 "petrol-station\0"
47 "atm-bank-exchange\0"
48 "toilet-rest-area\0"
49 "hospital-health-care-facility\0"
50 "eat-drink|restaurant\0" // subcategories always start after relative parent category
51 "eat-drink|coffee-tea\0"
52 "eat-drink|snacks-fast-food\0"
53 "transport|airport"
54 "\0";
55
56static const int FIXED_CATEGORIES_indices[] = {
57 0, 10, 20, 35, 45, 59, 68, 84,
58 115, 136, 151, 169, 186, 216, 237, 258,
59 285, -1
60};
61
62static const char * const NokiaIcon = "nokiaIcon";
63static const char * const IconPrefix = "iconPrefix";
64static const char * const NokiaIconGenerated = "nokiaIconGenerated";
65
66static const char * const IconThemeKey = "places.icons.theme";
67static const char * const LocalDataPathKey = "places.local_data_path";
68
70{
71public:
73 bool parse(const QString &fileName);
74
75 QPlaceCategoryTree tree() const { return m_tree; }
76 QHash<QString, QUrl> restIdToIconHash() const { return m_restIdToIconHash; }
77
79
80private:
81 void processCategory(int level, const QString &id,
82 const QString &parentId = QString());
83
84 QJsonObject m_exploreObject;
85 QPlaceCategoryTree m_tree;
86 QString m_errorString;
87
88 QHash<QString, QUrl> m_restIdToIconHash;
89};
90
94
95bool CategoryParser::parse(const QString &fileName)
96{
97 m_exploreObject = QJsonObject();
98 m_tree.clear();
99 m_errorString.clear();
100
101 QFile mappingFile(fileName);
102
103 if (mappingFile.open(QIODevice::ReadOnly)) {
104 QJsonDocument document = QJsonDocument::fromJson(mappingFile.readAll());
105 if (document.isObject()) {
106 QJsonObject docObject = document.object();
107 if (docObject.contains(QStringLiteral("offline_explore"))) {
108 m_exploreObject = docObject.value(QStringLiteral("offline_explore"))
109 .toObject();
110 if (m_exploreObject.contains(QStringLiteral("ROOT"))) {
111 processCategory(0, QString());
112 return true;
113 }
114 } else {
115 m_errorString = fileName
116 + QStringLiteral("does not contain the offline_explore property");
117 return false;
118 }
119 } else {
120 m_errorString = fileName + QStringLiteral("is not an json object");
121 return false;
122 }
123 }
124 m_errorString = QString::fromLatin1("Unable to open ") + fileName;
125 return false;
126}
127
128void CategoryParser::processCategory(int level, const QString &id, const QString &parentId)
129{
130 //We are basing the tree on a DAG from the input file, however we are simplyfing
131 //this into a 2 level tree, and a given category only has one parent
132 //
133 // A->B->Z
134 // A->C->Z
135 // Z in this case is not in the tree because it is 3 levels deep.
136 //
137 // X->Z
138 // Y->Z
139 // Only one of these is shown in the tree since Z can only have one parent
140 // the choice made between X and Y is arbitrary.
141 const int maxLevel = 2;
143 node.category.setCategoryId(id);
144 node.parentId = parentId;
145
146 m_tree.insert(node.category.categoryId(), node);
147 //this is simply to mark the node as being visited.
148 //a proper assignment to the tree happens at the end of function
149
150 QJsonObject categoryJson = m_exploreObject.value(id.isEmpty()
151 ? QStringLiteral("ROOT") : id).toObject();
152 QJsonArray children = categoryJson.value(QStringLiteral("children")).toArray();
153
154 if (level + 1 <= maxLevel && !categoryJson.contains(QStringLiteral("final"))) {
155 for (int i = 0; i < children.count(); ++i) {
156 QString childId = children.at(i).toString();
157 if (!m_tree.contains(childId)) {
158 node.childIds.append(childId);
159 processCategory(level + 1, childId, id);
160 }
161 }
162 }
163
164 m_tree.insert(node.category.categoryId(), node);
165}
166
167QPlaceManagerEngineNokiaV2::QPlaceManagerEngineNokiaV2(
168 QGeoNetworkAccessManager *networkManager,
169 const QVariantMap &parameters,
170 QGeoServiceProvider::Error *error,
171 QString *errorString)
172 : QPlaceManagerEngine(parameters)
173 , m_manager(networkManager)
174 , m_uriProvider(new QGeoUriProvider(this, parameters, QStringLiteral("here.places.host"), PLACES_HOST))
175{
176 Q_ASSERT(networkManager);
177 m_manager->setParent(this);
178
179 m_locales.append(QLocale());
180
181 m_apiKey = parameters.value(QStringLiteral("here.apiKey")).toString();
182
183 m_theme = parameters.value(IconThemeKey, QString()).toString();
184
185 if (m_theme == QStringLiteral("default"))
186 m_theme.clear();
187
188 m_localDataPath = parameters.value(LocalDataPathKey, QString()).toString();
189 if (m_localDataPath.isEmpty()) {
190 QStringList dataLocations = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
191
192 if (!dataLocations.isEmpty() && !dataLocations.first().isEmpty()) {
193 m_localDataPath = dataLocations.first()
194 + QStringLiteral("/here/qtlocation/data");
195 }
196 }
197
198 if (error)
199 *error = QGeoServiceProvider::NoError;
200
201 if (errorString)
202 errorString->clear();
203}
204
206
208{
209 QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() +
210 QStringLiteral("/places/v1/places/") + placeId);
211
212 QUrlQuery queryItems;
213
214 queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html"));
215 //queryItems.append(qMakePair<QString, QString>(QStringLiteral("size"), QString::number(5)));
216 //queryItems.append(qMakePair<QString, QString>(QStringLiteral("image_dimensions"), QStringLiteral("w64-h64,w100")));
217
218 requestUrl.setQuery(queryItems);
219
220 QNetworkReply *networkReply = sendRequest(requestUrl);
221
222 QPlaceDetailsReplyImpl *reply = new QPlaceDetailsReplyImpl(networkReply, this);
223 reply->setPlaceId(placeId);
224 connect(reply, &QPlaceDetailsReplyImpl::finished,
225 this, &QPlaceManagerEngineNokiaV2::replyFinished);
226 connect(reply, &QPlaceDetailsReplyImpl::errorOccurred,
227 this, &QPlaceManagerEngineNokiaV2::replyError);
228
229 return reply;
230}
231
232QPlaceContentReply *QPlaceManagerEngineNokiaV2::getPlaceContent(const QPlaceContentRequest &request)
233{
234 QNetworkReply *networkReply = nullptr;
235
236 if (request.contentContext().userType() == qMetaTypeId<QUrl>()) {
237 QUrl u = request.contentContext().value<QUrl>();
238
239 networkReply = sendRequest(u);
240 } else {
241 QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() +
242 QStringLiteral("/places/v1/places/") + request.placeId() +
243 QStringLiteral("/media/"));
244
245 QUrlQuery queryItems;
246
247 switch (request.contentType()) {
248 case QPlaceContent::ImageType:
249 requestUrl.setPath(requestUrl.path() + QStringLiteral("images"));
250
251 queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html"));
252
253 if (request.limit() > 0)
254 queryItems.addQueryItem(QStringLiteral("size"), QString::number(request.limit()));
255
256 //queryItems.append(qMakePair<QString, QString>(QStringLiteral("image_dimensions"), QStringLiteral("w64-h64,w100")));
257
258 requestUrl.setQuery(queryItems);
259
260 networkReply = sendRequest(requestUrl);
261 break;
262 case QPlaceContent::ReviewType:
263 requestUrl.setPath(requestUrl.path() + QStringLiteral("reviews"));
264
265 queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html"));
266
267 if (request.limit() > 0)
268 queryItems.addQueryItem(QStringLiteral("size"), QString::number(request.limit()));
269
270 requestUrl.setQuery(queryItems);
271
272 networkReply = sendRequest(requestUrl);
273 break;
274 case QPlaceContent::EditorialType:
275 requestUrl.setPath(requestUrl.path() + QStringLiteral("editorials"));
276
277 queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html"));
278
279 if (request.limit() > 0)
280 queryItems.addQueryItem(QStringLiteral("size"), QString::number(request.limit()));
281
282 requestUrl.setQuery(queryItems);
283
284 networkReply = sendRequest(requestUrl);
285 break;
286 case QPlaceContent::NoType:
287 default:
288 ;
289 }
290 }
291
292 QPlaceContentReply *reply = new QPlaceContentReplyImpl(request, networkReply, this);
293 connect(reply, &QPlaceContentReply::finished,
294 this, &QPlaceManagerEngineNokiaV2::replyFinished);
295 connect(reply, &QPlaceContentReply::errorOccurred,
296 this, &QPlaceManagerEngineNokiaV2::replyError);
297
298 if (!networkReply) {
299 QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection,
300 Q_ARG(QPlaceReply::Error, QPlaceReply::UnsupportedError),
301 Q_ARG(QString, QString("Retrieval of given content type not supported.")));
302 }
303
304 return reply;
305}
306
307static bool addAtForBoundingArea(const QGeoShape &area,
308 QUrlQuery *queryItems)
309{
310 QGeoCoordinate center = area.center();
311 if (!center.isValid())
312 return false;
313
314 queryItems->addQueryItem(QStringLiteral("at"),
315 QString::number(center.latitude()) +
316 QLatin1Char(',') +
317 QString::number(center.longitude()));
318 return true;
319}
320
321QPlaceSearchReply *QPlaceManagerEngineNokiaV2::search(const QPlaceSearchRequest &query)
322{
323 bool unsupported = false;
324
325 unsupported |= query.visibilityScope() != QLocation::UnspecifiedVisibility &&
326 query.visibilityScope() != QLocation::PublicVisibility;
327
328 // Both a search term and search categories are not supported.
329 unsupported |= !query.searchTerm().isEmpty() && !query.categories().isEmpty();
330
331 //only a recommendation id by itself is supported.
332 unsupported |= !query.recommendationId().isEmpty()
333 && (!query.searchTerm().isEmpty() || !query.categories().isEmpty()
334 || query.searchArea().type() != QGeoShape::UnknownType);
335
336 if (unsupported) {
337 QPlaceSearchReplyHere *reply = new QPlaceSearchReplyHere(query, 0, this);
338 connect(reply, &QPlaceSearchReplyHere::finished,
339 this, &QPlaceManagerEngineNokiaV2::replyFinished);
340 connect(reply, &QPlaceSearchReplyHere::errorOccurred,
341 this, &QPlaceManagerEngineNokiaV2::replyError);
342 QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection,
343 Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError),
344 Q_ARG(QString, "Unsupported search request options specified."));
345 return reply;
346 }
347
348 QUrlQuery queryItems;
349
350 // Check that the search area is valid for all searches except recommendation and proposed
351 // searches, which do not need search centers.
352 if (query.recommendationId().isEmpty() && !query.searchContext().isValid()) {
353 if (!addAtForBoundingArea(query.searchArea(), &queryItems)) {
354 QPlaceSearchReplyHere *reply = new QPlaceSearchReplyHere(query, 0, this);
355 connect(reply, &QPlaceSearchReplyHere::finished,
356 this, &QPlaceManagerEngineNokiaV2::replyFinished);
357 connect(reply, &QPlaceSearchReplyHere::errorOccurred,
358 this, &QPlaceManagerEngineNokiaV2::replyError);
359 QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection,
360 Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError),
361 Q_ARG(QString, "Invalid search area provided"));
362 return reply;
363 }
364 }
365
366 QNetworkReply *networkReply = nullptr;
367
368 if (query.searchContext().userType() == qMetaTypeId<QUrl>()) {
369 // provided search context
370 QUrl u = query.searchContext().value<QUrl>();
371
372 typedef QPair<QString, QString> QueryItem;
373 const QList<QueryItem> queryItemList = queryItems.queryItems(QUrl::FullyEncoded);
374 queryItems = QUrlQuery(u);
375 for (const QueryItem &item : queryItemList)
376 queryItems.addQueryItem(item.first, item.second);
377
378 if (query.limit() > 0)
379 queryItems.addQueryItem(QStringLiteral("size"), QString::number(query.limit()));
380
381 u.setQuery(queryItems);
382
383 networkReply = sendRequest(u);
384 } else if (!query.searchTerm().isEmpty()) {
385 // search term query
386 QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() +
387 QStringLiteral("/places/v1/discover/search"));
388
389 queryItems.addQueryItem(QStringLiteral("q"), query.searchTerm());
390 queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html"));
391
392 if (query.limit() > 0) {
393 queryItems.addQueryItem(QStringLiteral("size"),
394 QString::number(query.limit()));
395 }
396
397 requestUrl.setQuery(queryItems);
398
399 QNetworkReply *networkReply = sendRequest(requestUrl);
400
401 QPlaceSearchReplyHere *reply = new QPlaceSearchReplyHere(query, networkReply, this);
402 connect(reply, &QPlaceSearchReplyHere::finished,
403 this, &QPlaceManagerEngineNokiaV2::replyFinished);
404 connect(reply, &QPlaceSearchReplyHere::errorOccurred,
405 this, &QPlaceManagerEngineNokiaV2::replyError);
406
407 return reply;
408 } else if (!query.recommendationId().isEmpty()) {
409 QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() +
410 QStringLiteral("/places/v1/places/") + query.recommendationId() +
411 QStringLiteral("/related/recommended"));
412
413 queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html"));
414
415 requestUrl.setQuery(queryItems);
416
417 networkReply = sendRequest(requestUrl);
418 } else {
419 // category search
420 QUrl requestUrl(QStringLiteral("https://") + m_uriProvider->getCurrentHost() +
421 QStringLiteral("/places/v1/discover/explore"));
422
423 QStringList ids;
424 for (const QPlaceCategory &category : query.categories())
425 ids.append(category.categoryId());
426
427 QUrlQuery queryItems;
428
429 if (!ids.isEmpty())
430 queryItems.addQueryItem(QStringLiteral("cat"), ids.join(QStringLiteral(",")));
431
432 addAtForBoundingArea(query.searchArea(), &queryItems);
433
434 queryItems.addQueryItem(QStringLiteral("tf"), QStringLiteral("html"));
435
436 if (query.limit() > 0) {
437 queryItems.addQueryItem(QStringLiteral("size"),
438 QString::number(query.limit()));
439 }
440
441 requestUrl.setQuery(queryItems);
442
443 networkReply = sendRequest(requestUrl);
444 }
445
446 QPlaceSearchReplyHere *reply = new QPlaceSearchReplyHere(query, networkReply, this);
447 connect(reply, &QPlaceSearchReplyHere::finished,
448 this, &QPlaceManagerEngineNokiaV2::replyFinished);
449 connect(reply, &QPlaceSearchReplyHere::errorOccurred,
450 this, &QPlaceManagerEngineNokiaV2::replyError);
451
452 return reply;
453}
454
456{
457 bool unsupported = false;
458
459 unsupported |= query.visibilityScope() != QLocation::UnspecifiedVisibility &&
460 query.visibilityScope() != QLocation::PublicVisibility;
461
462 unsupported |= !query.categories().isEmpty();
463 unsupported |= !query.recommendationId().isEmpty();
464
465 if (unsupported) {
466 QPlaceSearchSuggestionReplyImpl *reply = new QPlaceSearchSuggestionReplyImpl(0, this);
467 connect(reply, &QPlaceSearchSuggestionReplyImpl::finished,
468 this, &QPlaceManagerEngineNokiaV2::replyFinished);
469 connect(reply, &QPlaceSearchSuggestionReplyImpl::errorOccurred,
470 this, &QPlaceManagerEngineNokiaV2::replyError);
471 QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection,
472 Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError),
473 Q_ARG(QString, "Unsupported search request options specified."));
474 return reply;
475 }
476
477 QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() +
478 QStringLiteral("/places/v1/suggest"));
479
480 QUrlQuery queryItems;
481
482 queryItems.addQueryItem(QStringLiteral("q"), query.searchTerm());
483
484 if (!addAtForBoundingArea(query.searchArea(), &queryItems)) {
485 QPlaceSearchSuggestionReplyImpl *reply = new QPlaceSearchSuggestionReplyImpl(0, this);
486 connect(reply, &QPlaceSearchSuggestionReplyImpl::finished,
487 this, &QPlaceManagerEngineNokiaV2::replyFinished);
488 connect(reply, &QPlaceSearchSuggestionReplyImpl::errorOccurred,
489 this, &QPlaceManagerEngineNokiaV2::replyError);
490 QMetaObject::invokeMethod(reply, "setError", Qt::QueuedConnection,
491 Q_ARG(QPlaceReply::Error, QPlaceReply::BadArgumentError),
492 Q_ARG(QString, "Invalid search area provided"));
493 return reply;
494 }
495
496 requestUrl.setQuery(queryItems);
497
498 QNetworkReply *networkReply = sendRequest(requestUrl);
499
500 QPlaceSearchSuggestionReplyImpl *reply = new QPlaceSearchSuggestionReplyImpl(networkReply, this);
501 connect(reply, &QPlaceSearchSuggestionReplyImpl::finished,
502 this, &QPlaceManagerEngineNokiaV2::replyFinished);
503 connect(reply, &QPlaceSearchSuggestionReplyImpl::errorOccurred,
504 this, &QPlaceManagerEngineNokiaV2::replyError);
505
506 return reply;
507}
508
510{
511 if (m_categoryReply)
512 return m_categoryReply.data();
513
514 m_tempTree.clear();
515 CategoryParser parser;
516
517 if (parser.parse(m_localDataPath + QStringLiteral("/offline/offline-mapping.json"))) {
518 m_tempTree = parser.tree();
519 } else {
520 PlaceCategoryNode rootNode;
521
522 for (int i = 0; FIXED_CATEGORIES_indices[i] != -1; ++i) {
523 const QString id = QString::fromLatin1(FIXED_CATEGORIES_string +
524 FIXED_CATEGORIES_indices[i]);
525
526 int subCatDivider = id.indexOf(QChar('|'));
527 if (subCatDivider >= 0) {
528 // found a sub category
529 const QString subCategoryId = id.mid(subCatDivider+1);
530 const QString parentCategoryId = id.left(subCatDivider);
531
532 if (m_tempTree.contains(parentCategoryId)) {
534 node.category.setCategoryId(subCategoryId);
535 node.parentId = parentCategoryId;
536
537 // find parent
538 PlaceCategoryNode &parent = m_tempTree[parentCategoryId];
539 parent.childIds.append(subCategoryId);
540 m_tempTree.insert(subCategoryId, node);
541 }
542
543 } else {
545 node.category.setCategoryId(id);
546
547 m_tempTree.insert(id, node);
548 rootNode.childIds.append(id);
549 }
550 }
551
552 m_tempTree.insert(QString(), rootNode);
553 }
554
555 //request all categories in the tree from the server
556 //because we don't want the root node, we skip it
557 for (auto it = m_tempTree.keyBegin(), end = m_tempTree.keyEnd(); it != end; ++it) {
558 if (*it == QString())
559 continue;
560 QUrl requestUrl(QString::fromLatin1("https://") + m_uriProvider->getCurrentHost() +
561 QStringLiteral("/places/v1/categories/places/") + *it);
562 QNetworkReply *networkReply = sendRequest(requestUrl);
563 connect(networkReply, &QNetworkReply::finished,
564 this, &QPlaceManagerEngineNokiaV2::categoryReplyFinished);
565 connect(networkReply, &QNetworkReply::errorOccurred,
566 this, &QPlaceManagerEngineNokiaV2::categoryReplyError);
567
568 m_categoryRequests.insert(*it, networkReply);
569 }
570
571 QPlaceCategoriesReplyHere *reply = new QPlaceCategoriesReplyHere(this);
572 connect(reply, &QPlaceCategoriesReplyHere::finished,
573 this, &QPlaceManagerEngineNokiaV2::replyFinished);
574 connect(reply, &QPlaceCategoriesReplyHere::errorOccurred,
575 this, &QPlaceManagerEngineNokiaV2::replyError);
576
577 m_categoryReply = reply;
578 return reply;
579}
580
581QString QPlaceManagerEngineNokiaV2::parentCategoryId(const QString &categoryId) const
582{
583 return m_categoryTree.value(categoryId).parentId;
584}
585
587{
588 return m_categoryTree.value(categoryId).childIds;
589}
590
591QPlaceCategory QPlaceManagerEngineNokiaV2::category(const QString &categoryId) const
592{
593 return m_categoryTree.value(categoryId).category;
594}
595
597{
598 QList<QPlaceCategory> results;
599 for (const QString &childId : m_categoryTree.value(parentId).childIds)
600 results.append(m_categoryTree.value(childId).category);
601 return results;
602}
603
605{
606 return m_locales;
607}
608
609void QPlaceManagerEngineNokiaV2::setLocales(const QList<QLocale> &locales)
610{
611 m_locales = locales;
612}
613
614QPlaceIcon QPlaceManagerEngineNokiaV2::icon(const QString &remotePath,
615 const QList<QPlaceCategory> &categories) const
616{
617 QPlaceIcon icon;
618 QVariantMap params;
619
620 QRegularExpression rx("(.*)(/icons/categories/.*)");
621 QRegularExpressionMatch match = rx.match(remotePath);
622
623 QString iconPrefix;
624 QString nokiaIcon;
625 if (match.hasMatch() && !match.capturedView(1).isEmpty() && !match.capturedView(2).isEmpty()) {
626 iconPrefix = match.captured(1);
627 nokiaIcon = match.captured(2);
628
629 if (QFile::exists(m_localDataPath + nokiaIcon))
630 iconPrefix = QString::fromLatin1("file://") + m_localDataPath;
631
632 params.insert(NokiaIcon, nokiaIcon);
633 params.insert(IconPrefix, iconPrefix);
634
635 for (const QPlaceCategory &category : categories) {
636 if (category.icon().parameters().value(NokiaIcon) == nokiaIcon) {
637 params.insert(NokiaIconGenerated, true);
638 break;
639 }
640 }
641 } else {
642 QString path = remotePath + (!m_theme.isEmpty()
643 ? QLatin1Char('.') + m_theme : QString());
644 params.insert(QPlaceIcon::SingleUrl, QUrl(path));
645
646 if (!nokiaIcon.isEmpty()) {
647 params.insert(NokiaIcon, nokiaIcon);
648 params.insert(IconPrefix, iconPrefix);
649 params.insert(NokiaIconGenerated, true);
650 }
651 }
652
653 icon.setParameters(params);
654
655 if (!icon.isEmpty())
656 icon.setManager(manager());
657
658 return icon;
659}
660
662 const QSize &size) const
663{
664 Q_UNUSED(size);
665 QVariantMap params = icon.parameters();
666 QString nokiaIcon = params.value(NokiaIcon).toString();
667
668 if (!nokiaIcon.isEmpty()) {
669 nokiaIcon.append(!m_theme.isEmpty() ?
670 QLatin1Char('.') + m_theme : QString());
671
672 if (params.contains(IconPrefix)) {
673 return QUrl(params.value(IconPrefix).toString() +
674 nokiaIcon);
675 } else {
676 return QUrl(QString::fromLatin1("file://") + m_localDataPath
677 + nokiaIcon);
678 }
679 }
680
681 return QUrl();
682}
683
684void QPlaceManagerEngineNokiaV2::replyFinished()
685{
686 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
687 if (reply)
688 emit finished(reply);
689}
690
691void QPlaceManagerEngineNokiaV2::replyError(QPlaceReply::Error error_, const QString &errorString)
692{
693 QPlaceReply *reply = qobject_cast<QPlaceReply *>(sender());
694 if (reply)
695 emit errorOccurred(reply, error_, errorString);
696}
697
698void QPlaceManagerEngineNokiaV2::categoryReplyFinished()
699{
700 QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
701 if (!reply)
702 return;
703
704 QString categoryId;
705
706 if (reply->error() == QNetworkReply::NoError) {
707 QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
708 if (!document.isObject()) {
709 if (m_categoryReply) {
710 QMetaObject::invokeMethod(m_categoryReply.data(), "setError", Qt::QueuedConnection,
711 Q_ARG(QPlaceReply::Error, QPlaceReply::ParseError),
712 Q_ARG(QString, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, PARSE_ERROR)));
713 }
714 return;
715 }
716
717 QJsonObject category = document.object();
718
719 categoryId = category.value(QStringLiteral("categoryId")).toString();
720 if (m_tempTree.contains(categoryId)) {
721 PlaceCategoryNode node = m_tempTree.value(categoryId);
722 node.category.setName(category.value(QStringLiteral("name")).toString());
723 node.category.setCategoryId(categoryId);
724 node.category.setIcon(icon(category.value(QStringLiteral("icon")).toString()));
725
726 m_tempTree.insert(categoryId, node);
727 }
728 } else {
729 categoryId = m_categoryRequests.key(reply);
730 PlaceCategoryNode rootNode = m_tempTree.value(QString());
731 rootNode.childIds.removeAll(categoryId);
732 m_tempTree.insert(QString(), rootNode);
733 m_tempTree.remove(categoryId);
734 }
735
736 m_categoryRequests.remove(categoryId);
737 reply->deleteLater();
738
739 if (m_categoryRequests.isEmpty()) {
740 m_categoryTree = m_tempTree;
741 m_tempTree.clear();
742
743 if (m_categoryReply)
744 m_categoryReply.data()->emitFinished();
745 }
746}
747
748void QPlaceManagerEngineNokiaV2::categoryReplyError()
749{
750 if (m_categoryReply) {
751 QMetaObject::invokeMethod(m_categoryReply.data(), "setError", Qt::QueuedConnection,
752 Q_ARG(QPlaceReply::Error, QPlaceReply::CommunicationError),
753 Q_ARG(QString, QCoreApplication::translate(NOKIA_PLUGIN_CONTEXT_NAME, NETWORK_ERROR)));
754 }
755}
756
757QNetworkReply *QPlaceManagerEngineNokiaV2::sendRequest(const QUrl &url)
758{
759 QUrlQuery queryItems(url);
760 queryItems.addQueryItem(QStringLiteral("apiKey"), m_apiKey);
761
762 QUrl requestUrl = url;
763 requestUrl.setQuery(queryItems);
764
765 QNetworkRequest request;
766 request.setUrl(requestUrl);
767
768 request.setRawHeader("Accept", "application/json");
769 request.setRawHeader("Accept-Language", createLanguageString());
770
771 return m_manager->get(request);
772}
773
774QByteArray QPlaceManagerEngineNokiaV2::createLanguageString() const
775{
776 QByteArray language;
777
778 QList<QLocale> locales = m_locales;
779 if (locales.isEmpty())
780 locales << QLocale();
781
782 for (const QLocale &loc : std::as_const(locales)) {
783 language.append(loc.name().replace(2, 1, QLatin1Char('-')).toLatin1());
784 language.append(", ");
785 }
786 language.chop(2);
787
788 return language;
789}
790
791QT_END_NAMESPACE
bool parse(const QString &fileName)
QHash< QString, QUrl > restIdToIconHash() const
QString errorString() const
QPlaceCategoryTree tree() const
QStringList childCategoryIds(const QString &categoryId) const override
Returns the child category identifiers of the category corresponding to categoryId.
QString parentCategoryId(const QString &categoryId) const override
Returns the parent category identifier of the category corresponding to categoryId.
QPlaceSearchSuggestionReply * searchSuggestions(const QPlaceSearchRequest &query) override
Requests a set of search term suggestions according to the parameters specified in request.
QPlaceReply * initializeCategories() override
Initializes the categories of the manager engine.
QPlaceDetailsReply * getPlaceDetails(const QString &placeId) override
Retrieves details of place corresponding to the given placeId.
QList< QPlaceCategory > childCategories(const QString &parentId) const override
Returns a list of categories that are children of the category corresponding to parentId.
QUrl constructIconUrl(const QPlaceIcon &icon, const QSize &size) const override
QUrl QPlaceManagerEngine::constructIconUrl(const QPlaceIcon &icon, const QSize &size)
QPlaceSearchReply * search(const QPlaceSearchRequest &query) override
Searches for places according to the parameters specified in request.
void setLocales(const QList< QLocale > &locales) override
Set the list of preferred locales.
QList< QLocale > locales() const override
Returns a list of preferred locales.
QPlaceCategory category(const QString &categoryId) const override
Returns the category corresponding to the given categoryId.
QPlaceIcon icon(const QString &remotePath, const QList< QPlaceCategory > &categories=QList< QPlaceCategory >()) const
static const char *const LocalDataPathKey
static bool addAtForBoundingArea(const QGeoShape &area, QUrlQuery *queryItems)
static const char *const IconThemeKey
static const char *const IconPrefix
static const char *const NokiaIcon
static QT_BEGIN_NAMESPACE const char FIXED_CATEGORIES_string[]
static const int FIXED_CATEGORIES_indices[]
static const char *const NokiaIconGenerated
QMap< QString, PlaceCategoryNode > QPlaceCategoryTree