1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtPositioning module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "qdeclarativepositionsource_p.h"
41 #include "qdeclarativeposition_p.h"
42
43 #include <QtCore/QCoreApplication>
44 #include <QtQml/qqmlinfo.h>
45 #include <QtQml/qqml.h>
46 #include <QtPositioning/qnmeapositioninfosource.h>
47 #include <qdeclarativepluginparameter_p.h>
48 #include <QFile>
49 #include <QtNetwork/QTcpSocket>
50 #include <QTimer>
51
52 QT_BEGIN_NAMESPACE
53
54 /*!
55 \qmltype PositionSource
56 //! \instantiates QDeclarativePositionSource
57 \inqmlmodule QtPositioning
58 \since 5.2
59
60 \brief The PositionSource type provides the device's current position.
61
62 The PositionSource type provides information about the user device's
63 current position. The position is available as a \l{Position} type, which
64 contains all the standard parameters typically available from GPS and other
65 similar systems, including longitude, latitude, speed and accuracy details.
66
67 As different position sources are available on different platforms and
68 devices, these are categorized by their basic type (Satellite, NonSatellite,
69 and AllPositioningMethods). The available methods for the current platform
70 can be enumerated in the \l{supportedPositioningMethods} property.
71
72 To indicate which methods are suitable for your application, set the
73 \l{preferredPositioningMethods} property. If the preferred methods are not
74 available, the default source of location data for the platform will be
75 chosen instead. If no default source is available (because none are installed
76 for the runtime platform, or because it is disabled), the \l{valid} property
77 will be set to false.
78
79 The \l updateInterval property can then be used to indicate how often your
80 application wishes to receive position updates. The \l{start}(),
81 \l{stop}() and \l{update}() methods can be used to control the operation
82 of the PositionSource, as well as the \l{active} property, which when set
83 is equivalent to calling \l{start}() or \l{stop}().
84
85 When the PositionSource is active, position updates can be retrieved
86 either by simply using the \l{position} property in a binding (as the
87 value of another item's property), or by providing an implementation of
88 the \c {onPositionChanged} signal-handler.
89
90 \section2 Example Usage
91
92 The following example shows a simple PositionSource used to receive
93 updates every second and print the longitude and latitude out to
94 the console.
95
96 \code
97 PositionSource {
98 id: src
99 updateInterval: 1000
100 active: true
101
102 onPositionChanged: {
103 var coord = src.position.coordinate;
104 console.log("Coordinate:", coord.longitude, coord.latitude);
105 }
106 }
107 \endcode
108
109 The \l{geoflickr}{GeoFlickr} example application shows how to use
110 a PositionSource in your application to retrieve local data for users
111 from a REST web service.
112
113 \sa {QtPositioning::Position}, {QGeoPositionInfoSource}, {PluginParameter}
114
115 */
116
117 /*!
118 \qmlsignal PositionSource::updateTimeout()
119
120 If \l update() was called, this signal is emitted if the current position could not be
121 retrieved within a certain amount of time.
122
123 If \l start() was called, this signal is emitted if the position engine determines that
124 it is not able to provide further regular updates.
125
126 \since Qt Positioning 5.5
127
128 \sa QGeoPositionInfoSource::updateTimeout()
129 */
130
131
QDeclarativePositionSource()132 QDeclarativePositionSource::QDeclarativePositionSource()
133 : m_positionSource(0), m_preferredPositioningMethods(NoPositioningMethods), m_nmeaFile(0),
134 m_nmeaSocket(0), m_active(false), m_singleUpdate(false), m_updateInterval(0),
135 m_sourceError(NoError)
136 {
137 }
138
~QDeclarativePositionSource()139 QDeclarativePositionSource::~QDeclarativePositionSource()
140 {
141 delete m_nmeaFile;
142 delete m_nmeaSocket;
143 delete m_positionSource;
144 }
145
146
147 /*!
148 \qmlproperty string PositionSource::name
149
150 This property holds the unique internal name for the plugin currently
151 providing position information.
152
153 Setting the property causes the PositionSource to use a particular positioning provider. If
154 the PositionSource is active at the time that the name property is changed, it will become
155 inactive. If the specified positioning provider cannot be loaded the position source will
156 become invalid.
157
158 Changing the name property may cause the \l {updateInterval}, \l {supportedPositioningMethods}
159 and \l {preferredPositioningMethods} properties to change as well.
160 */
161
162
name() const163 QString QDeclarativePositionSource::name() const
164 {
165 if (m_positionSource)
166 return m_positionSource->sourceName();
167 else
168 return m_providerName;
169 }
170
setName(const QString & newName)171 void QDeclarativePositionSource::setName(const QString &newName)
172 {
173 if (m_positionSource && m_positionSource->sourceName() == newName)
174 return;
175
176 if (m_providerName == newName && m_providerName.isEmpty())
177 return; // previously attached to a default source, now requesting the same.
178
179 const QString previousName = name();
180 m_providerName = newName;
181
182 if (!m_componentComplete || !m_parametersInitialized) {
183 if (previousName != name())
184 emit nameChanged();
185 return;
186 }
187
188 tryAttach(newName, false);
189 }
190
191 /*!
192 \internal
193 */
tryAttach(const QString & newName,bool useFallback)194 void QDeclarativePositionSource::tryAttach(const QString &newName, bool useFallback)
195 {
196 const QString previousName = name();
197 const bool sourceExisted = m_positionSource;
198 m_providerName = newName;
199
200 int previousUpdateInterval = updateInterval();
201 PositioningMethods previousPositioningMethods = supportedPositioningMethods();
202 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
203
204 if (newName.isEmpty()) {
205 setSource(QGeoPositionInfoSource::createDefaultSource(parameterMap(), this));
206 } else {
207 setSource(QGeoPositionInfoSource::createSource(newName, parameterMap(), this));
208 if (!m_positionSource && useFallback)
209 setSource(QGeoPositionInfoSource::createDefaultSource(parameterMap(), this));
210 }
211
212 if (m_positionSource) {
213 connect(m_positionSource, SIGNAL(positionUpdated(QGeoPositionInfo)),
214 this, SLOT(positionUpdateReceived(QGeoPositionInfo)));
215 connect(m_positionSource, SIGNAL(error(QGeoPositionInfoSource::Error)),
216 this, SLOT(sourceErrorReceived(QGeoPositionInfoSource::Error)));
217 connect(m_positionSource, SIGNAL(updateTimeout()),
218 this, SLOT(updateTimeoutReceived()));
219
220 m_positionSource->setUpdateInterval(m_updateInterval);
221 m_positionSource->setPreferredPositioningMethods(
222 static_cast<QGeoPositionInfoSource::PositioningMethods>(int(m_preferredPositioningMethods)));
223
224 const QGeoPositionInfo &lastKnown = m_positionSource->lastKnownPosition();
225 if (lastKnown.isValid())
226 setPosition(lastKnown);
227 } else if (m_active) {
228 m_active = false;
229 emit activeChanged();
230 }
231
232 if (previousUpdateInterval != updateInterval())
233 emit updateIntervalChanged();
234
235 if (previousPreferredPositioningMethods != preferredPositioningMethods())
236 emit preferredPositioningMethodsChanged();
237
238 if (previousPositioningMethods != supportedPositioningMethods())
239 emit supportedPositioningMethodsChanged();
240
241 emit validityChanged();
242
243 if (m_active) { // implies m_positionSource
244 if (!sourceExisted) {
245 QTimer::singleShot(0, this, SLOT(start())); // delay ensures all properties have been set
246 } else {
247 m_active = false;
248 emit activeChanged();
249 }
250 }
251
252 if (previousName != name())
253 emit nameChanged();
254 }
255
256 /*!
257 \qmlproperty bool PositionSource::valid
258
259 This property is true if the PositionSource object has acquired a valid
260 backend plugin to provide data. If false, other methods on the PositionSource
261 will have no effect.
262
263 Applications should check this property to determine whether positioning is
264 available and enabled on the runtime platform, and react accordingly.
265 */
isValid() const266 bool QDeclarativePositionSource::isValid() const
267 {
268 return (m_positionSource != 0);
269 }
270
setNmeaSource(const QUrl & nmeaSource)271 void QDeclarativePositionSource::setNmeaSource(const QUrl &nmeaSource)
272 {
273 if (nmeaSource.scheme() == QLatin1String("socket")) {
274 if (m_nmeaSocket
275 && nmeaSource.host() == m_nmeaSocket->peerName()
276 && nmeaSource.port() == m_nmeaSocket->peerPort()) {
277 return;
278 }
279
280 delete m_nmeaSocket;
281 m_nmeaSocket = new QTcpSocket();
282
283 connect(m_nmeaSocket, &QAbstractSocket::errorOccurred,
284 this, &QDeclarativePositionSource::socketError);
285 connect(m_nmeaSocket, &QTcpSocket::connected,
286 this, &QDeclarativePositionSource::socketConnected);
287
288 m_nmeaSocket->connectToHost(nmeaSource.host(), nmeaSource.port(), QTcpSocket::ReadOnly);
289 } else {
290 // Strip the filename. This is clumsy but the file may be prefixed in several
291 // ways: "file:///", "qrc:///", "/", "" in platform dependent manner.
292 QString localFileName = nmeaSource.toString();
293 if (!QFile::exists(localFileName)) {
294 if (localFileName.startsWith(QStringLiteral("qrc:///"))) {
295 localFileName.remove(0, 7);
296 } else if (localFileName.startsWith(QStringLiteral("file:///"))) {
297 localFileName.remove(0, 7);
298 } else if (localFileName.startsWith(QStringLiteral("qrc:/"))) {
299 localFileName.remove(0, 5);
300 }
301 if (!QFile::exists(localFileName) && localFileName.startsWith('/')) {
302 localFileName.remove(0,1);
303 }
304 }
305 if (m_nmeaFileName == localFileName)
306 return;
307 m_nmeaFileName = localFileName;
308
309 PositioningMethods previousPositioningMethods = supportedPositioningMethods();
310
311 // The current position source needs to be deleted
312 // because QNmeaPositionInfoSource can be bound only to a one file.
313 delete m_nmeaSocket;
314 m_nmeaSocket = 0;
315 setSource(nullptr);
316 setPosition(QGeoPositionInfo());
317 // Create the NMEA source based on the given data. QML has automatically set QUrl
318 // type to point to correct path. If the file is not found, check if the file actually
319 // was an embedded resource file.
320 delete m_nmeaFile;
321 m_nmeaFile = new QFile(localFileName);
322 if (!m_nmeaFile->exists()) {
323 localFileName.prepend(':');
324 m_nmeaFile->setFileName(localFileName);
325 }
326 if (m_nmeaFile->exists()) {
327 #ifdef QDECLARATIVE_POSITION_DEBUG
328 qDebug() << "QDeclarativePositionSource NMEA File was found: " << localFileName;
329 #endif
330 setSource(new QNmeaPositionInfoSource(QNmeaPositionInfoSource::SimulationMode));
331 (qobject_cast<QNmeaPositionInfoSource *>(m_positionSource))->setUserEquivalentRangeError(2.5); // it is internally multiplied by 2 in qlocationutils_readGga
332 (qobject_cast<QNmeaPositionInfoSource *>(m_positionSource))->setDevice(m_nmeaFile);
333 connect(m_positionSource, SIGNAL(positionUpdated(QGeoPositionInfo)),
334 this, SLOT(positionUpdateReceived(QGeoPositionInfo)));
335 connect(m_positionSource, SIGNAL(error(QGeoPositionInfoSource::Error)),
336 this, SLOT(sourceErrorReceived(QGeoPositionInfoSource::Error)));
337 connect(m_positionSource, SIGNAL(updateTimeout()),
338 this, SLOT(updateTimeoutReceived()));
339
340 setPosition(m_positionSource->lastKnownPosition());
341 if (m_active && !m_singleUpdate) {
342 // Keep on updating even though source changed
343 QTimer::singleShot(0, this, SLOT(start()));
344 }
345 } else {
346 qmlWarning(this) << QStringLiteral("Nmea file not found") << localFileName;
347 #ifdef QDECLARATIVE_POSITION_DEBUG
348 qDebug() << "QDeclarativePositionSource NMEA File was not found: " << localFileName;
349 #endif
350 if (m_active) {
351 m_active = false;
352 m_singleUpdate = false;
353 emit activeChanged();
354 }
355 }
356
357 if (previousPositioningMethods != supportedPositioningMethods())
358 emit supportedPositioningMethodsChanged();
359 }
360
361 m_nmeaSource = nmeaSource;
362 emit nmeaSourceChanged();
363 }
364
365 /*!
366 \internal
367 */
socketConnected()368 void QDeclarativePositionSource::socketConnected()
369 {
370 #ifdef QDECLARATIVE_POSITION_DEBUG
371 qDebug() << "Socket connected: " << m_nmeaSocket->peerName();
372 #endif
373 PositioningMethods previousPositioningMethods = supportedPositioningMethods();
374
375 // The current position source needs to be deleted
376 // because QNmeaPositionInfoSource can be bound only to a one file.
377 delete m_nmeaFile;
378 m_nmeaFile = 0;
379 setSource(nullptr);
380
381 setSource(new QNmeaPositionInfoSource(QNmeaPositionInfoSource::RealTimeMode));
382 (qobject_cast<QNmeaPositionInfoSource *>(m_positionSource))->setDevice(m_nmeaSocket);
383
384 connect(m_positionSource, &QNmeaPositionInfoSource::positionUpdated,
385 this, &QDeclarativePositionSource::positionUpdateReceived);
386 connect(m_positionSource, SIGNAL(error(QGeoPositionInfoSource::Error)),
387 this, SLOT(sourceErrorReceived(QGeoPositionInfoSource::Error)));
388 connect(m_positionSource, SIGNAL(updateTimeout()),
389 this, SLOT(updateTimeoutReceived()));
390
391 setPosition(m_positionSource->lastKnownPosition());
392
393 if (m_active && !m_singleUpdate) {
394 // Keep on updating even though source changed
395 QTimer::singleShot(0, this, SLOT(start()));
396 }
397
398 if (previousPositioningMethods != supportedPositioningMethods())
399 emit supportedPositioningMethodsChanged();
400 }
401
402 /*!
403 \internal
404 */
socketError(QAbstractSocket::SocketError error)405 void QDeclarativePositionSource::socketError(QAbstractSocket::SocketError error)
406 {
407 m_nmeaSocket->deleteLater();
408 m_nmeaSocket = nullptr;
409
410 switch (error) {
411 case QAbstractSocket::UnknownSocketError:
412 m_sourceError = QDeclarativePositionSource::UnknownSourceError;
413 break;
414 case QAbstractSocket::SocketAccessError:
415 m_sourceError = QDeclarativePositionSource::AccessError;
416 break;
417 case QAbstractSocket::RemoteHostClosedError:
418 m_sourceError = QDeclarativePositionSource::ClosedError;
419 break;
420 default:
421 qWarning() << "Connection failed! QAbstractSocket::SocketError" << error;
422 m_sourceError = QDeclarativePositionSource::SocketError;
423 break;
424 }
425
426 emit sourceErrorChanged();
427 }
428
429
updateTimeoutReceived()430 void QDeclarativePositionSource::updateTimeoutReceived()
431 {
432 if (!m_active)
433 return;
434
435 if (m_singleUpdate) {
436 m_singleUpdate = false;
437
438 // only singleUpdate based timeouts change activity
439 // continuous updates may resume again (see QGeoPositionInfoSource::startUpdates())
440 m_active = false;
441 emit activeChanged();
442 }
443
444 emit updateTimeout();
445 }
446
447 /*!
448 \internal
449 */
onParameterInitialized()450 void QDeclarativePositionSource::onParameterInitialized()
451 {
452 m_parametersInitialized = true;
453 for (QDeclarativePluginParameter *p: qAsConst(m_parameters)) {
454 if (!p->isInitialized()) {
455 m_parametersInitialized = false;
456 break;
457 }
458 }
459
460 // If here, componentComplete has been called.
461 if (m_parametersInitialized)
462 tryAttach(m_providerName);
463 }
464
setPosition(const QGeoPositionInfo & pi)465 void QDeclarativePositionSource::setPosition(const QGeoPositionInfo &pi)
466 {
467 m_position.setPosition(pi);
468 emit positionChanged();
469 }
470
setSource(QGeoPositionInfoSource * source)471 void QDeclarativePositionSource::setSource(QGeoPositionInfoSource *source)
472 {
473 if (m_positionSource)
474 delete m_positionSource;
475
476 if (!source) {
477 m_positionSource = nullptr;
478 } else {
479 m_positionSource = source;
480 connect(m_positionSource, &QGeoPositionInfoSource::supportedPositioningMethodsChanged,
481 this, &QDeclarativePositionSource::supportedPositioningMethodsChanged);
482 }
483 }
484
parametersReady()485 bool QDeclarativePositionSource::parametersReady()
486 {
487 for (const QDeclarativePluginParameter *p: qAsConst(m_parameters)) {
488 if (!p->isInitialized())
489 return false;
490 }
491 return true;
492 }
493
494 /*!
495 \internal
496 */
parameterMap() const497 QVariantMap QDeclarativePositionSource::parameterMap() const
498 {
499 QVariantMap map;
500
501 for (int i = 0; i < m_parameters.size(); ++i) {
502 QDeclarativePluginParameter *parameter = m_parameters.at(i);
503 map.insert(parameter->name(), parameter->value());
504 }
505
506 return map;
507 }
508
509 /*!
510 \internal
511 */
setUpdateInterval(int updateInterval)512 void QDeclarativePositionSource::setUpdateInterval(int updateInterval)
513 {
514 if (m_positionSource) {
515 int previousUpdateInterval = m_positionSource->updateInterval();
516
517 m_updateInterval = updateInterval;
518
519 if (previousUpdateInterval != updateInterval) {
520 m_positionSource->setUpdateInterval(updateInterval);
521 if (previousUpdateInterval != m_positionSource->updateInterval())
522 emit updateIntervalChanged();
523 }
524 } else {
525 if (m_updateInterval != updateInterval) {
526 m_updateInterval = updateInterval;
527 emit updateIntervalChanged();
528 }
529 }
530 }
531
532 /*!
533 \qmlproperty url PositionSource::nmeaSource
534
535 This property holds the source for NMEA (National Marine Electronics Association)
536 position-specification data (file). One purpose of this property is to be of
537 development convenience.
538
539 Setting this property will override any other position source. Currently only
540 files local to the .qml -file are supported. The NMEA source is created in simulation mode,
541 meaning that the data and time information in the NMEA source data is used to provide
542 positional updates at the rate at which the data was originally recorded.
543
544 If nmeaSource has been set for a PositionSource object, there is no way to revert
545 back to non-file sources.
546 */
547
nmeaSource() const548 QUrl QDeclarativePositionSource::nmeaSource() const
549 {
550 return m_nmeaSource;
551 }
552
553 /*!
554 \qmlproperty int PositionSource::updateInterval
555
556 This property holds the desired interval between updates (milliseconds).
557
558 \sa {QGeoPositionInfoSource::updateInterval()}
559 */
560
updateInterval() const561 int QDeclarativePositionSource::updateInterval() const
562 {
563 if (!m_positionSource)
564 return m_updateInterval;
565
566 return m_positionSource->updateInterval();
567 }
568
569 /*!
570 \qmlproperty enumeration PositionSource::supportedPositioningMethods
571
572 This property holds the supported positioning methods of the
573 current source.
574
575 \list
576 \li PositionSource.NoPositioningMethods - No positioning methods supported (no source).
577 \li PositionSource.SatellitePositioningMethods - Satellite-based positioning methods such as GPS are supported.
578 \li PositionSource.NonSatellitePositioningMethods - Non-satellite-based methods are supported.
579 \li PositionSource.AllPositioningMethods - Both satellite-based and non-satellite positioning methods are supported.
580 \endlist
581
582 */
583
supportedPositioningMethods() const584 QDeclarativePositionSource::PositioningMethods QDeclarativePositionSource::supportedPositioningMethods() const
585 {
586 if (m_positionSource) {
587 return static_cast<QDeclarativePositionSource::PositioningMethods>(
588 int(m_positionSource->supportedPositioningMethods()));
589 }
590 return QDeclarativePositionSource::NoPositioningMethods;
591 }
592
593 /*!
594 \qmlproperty enumeration PositionSource::preferredPositioningMethods
595
596 This property holds the preferred positioning methods of the
597 current source.
598
599 \list
600 \li PositionSource.NoPositioningMethods - No positioning method is preferred.
601 \li PositionSource.SatellitePositioningMethods - Satellite-based positioning methods such as GPS should be preferred.
602 \li PositionSource.NonSatellitePositioningMethods - Non-satellite-based methods should be preferred.
603 \li PositionSource.AllPositioningMethods - Any positioning methods are acceptable.
604 \endlist
605
606 */
607
setPreferredPositioningMethods(PositioningMethods methods)608 void QDeclarativePositionSource::setPreferredPositioningMethods(PositioningMethods methods)
609 {
610 if (m_positionSource) {
611 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
612
613 m_preferredPositioningMethods = methods;
614
615 if (previousPreferredPositioningMethods != methods) {
616 m_positionSource->setPreferredPositioningMethods(
617 static_cast<QGeoPositionInfoSource::PositioningMethods>(int(methods)));
618 if (previousPreferredPositioningMethods != m_positionSource->preferredPositioningMethods())
619 emit preferredPositioningMethodsChanged();
620 }
621 } else {
622 if (m_preferredPositioningMethods != methods) {
623 m_preferredPositioningMethods = methods;
624 emit preferredPositioningMethodsChanged();
625 }
626 }
627 }
628
preferredPositioningMethods() const629 QDeclarativePositionSource::PositioningMethods QDeclarativePositionSource::preferredPositioningMethods() const
630 {
631 if (m_positionSource) {
632 return static_cast<QDeclarativePositionSource::PositioningMethods>(
633 int(m_positionSource->preferredPositioningMethods()));
634 }
635 return m_preferredPositioningMethods;
636 }
637
638 /*!
639 \qmlmethod PositionSource::start()
640
641 Requests updates from the location source.
642 Uses \l updateInterval if set, default interval otherwise.
643 If there is no source available, this method has no effect.
644
645 \sa stop, update, active
646 */
647
start()648 void QDeclarativePositionSource::start()
649 {
650 if (m_positionSource)
651 m_positionSource->startUpdates();
652
653 if (!m_active) {
654 m_active = true;
655 emit activeChanged();
656 }
657 }
658
659 /*!
660 \qmlmethod PositionSource::update()
661
662 A convenience method to request single update from the location source.
663 If there is no source available, this method has no effect.
664
665 If the position source is not active, it will be activated for as
666 long as it takes to receive an update, or until the request times
667 out. The request timeout period is source-specific.
668
669 \sa start, stop, active
670 */
671
update()672 void QDeclarativePositionSource::update()
673 {
674 if (m_positionSource) {
675 if (!m_active) {
676 m_active = true;
677 m_singleUpdate = true;
678 emit activeChanged();
679 }
680 // Use default timeout value. Set active before calling the
681 // update request because on some platforms there may
682 // be results immediately.
683 m_positionSource->requestUpdate();
684 }
685 }
686
687 /*!
688 \qmlmethod PositionSource::stop()
689
690 Stops updates from the location source.
691 If there is no source available or it is not active,
692 this method has no effect.
693
694 \sa start, update, active
695 */
696
stop()697 void QDeclarativePositionSource::stop()
698 {
699 if (m_positionSource) {
700 m_positionSource->stopUpdates();
701 if (m_active) {
702 m_active = false;
703 emit activeChanged();
704 }
705 }
706 }
707
708 /*!
709 \qmlproperty bool PositionSource::active
710
711 This property indicates whether the position source is active.
712 Setting this property to false equals calling \l stop, and
713 setting this property true equals calling \l start.
714
715 \sa start, stop, update
716 */
setActive(bool active)717 void QDeclarativePositionSource::setActive(bool active)
718 {
719 if (active == m_active)
720 return;
721
722 if (active)
723 QTimer::singleShot(0, this, SLOT(start())); // delay ensures all properties have been set
724 else
725 stop();
726 }
727
isActive() const728 bool QDeclarativePositionSource::isActive() const
729 {
730 return m_active;
731 }
732
733 /*!
734 \qmlproperty Position PositionSource::position
735
736 This property holds the last known positional data.
737 It is a read-only property.
738
739 The Position type has different positional member variables,
740 whose validity can be checked with appropriate validity functions
741 (for example sometimes an update does not have speed or altitude data).
742
743 However, whenever a \c {positionChanged} signal has been received, at least
744 position::coordinate::latitude, position::coordinate::longitude, and position::timestamp can
745 be assumed to be valid.
746
747 \sa start, stop, update
748 */
749
position()750 QDeclarativePosition *QDeclarativePositionSource::position()
751 {
752 return &m_position;
753 }
754
positionUpdateReceived(const QGeoPositionInfo & update)755 void QDeclarativePositionSource::positionUpdateReceived(const QGeoPositionInfo &update)
756 {
757 setPosition(update);
758
759 if (m_singleUpdate && m_active) {
760 m_active = false;
761 m_singleUpdate = false;
762 emit activeChanged();
763 }
764 }
765
766
767 /*!
768 \qmlproperty enumeration PositionSource::sourceError
769
770 This property holds the error which last occurred with the PositionSource.
771
772 \list
773 \li PositionSource.AccessError - The connection setup to the remote positioning backend failed because the
774 application lacked the required privileges.
775 \li PositionSource.ClosedError - The positioning backend closed the connection, which happens for example in case
776 the user is switching location services to off. As soon as the location service is re-enabled
777 regular updates will resume.
778 \li PositionSource.NoError - No error has occurred.
779 \li PositionSource.UnknownSourceError - An unidentified error occurred.
780 \li PositionSource.SocketError - An error occurred while connecting to an nmea source using a socket.
781 \endlist
782
783 */
784
sourceError() const785 QDeclarativePositionSource::SourceError QDeclarativePositionSource::sourceError() const
786 {
787 return m_sourceError;
788 }
789
positionSource() const790 QGeoPositionInfoSource *QDeclarativePositionSource::positionSource() const
791 {
792 return m_positionSource;
793 }
794
795 /*!
796 \qmlproperty list<PluginParameter> PositionSource::parameters
797 \default
798
799 This property holds the list of plugin parameters.
800
801 \since QtPositioning 5.14
802 */
parameters()803 QQmlListProperty<QDeclarativePluginParameter> QDeclarativePositionSource::parameters()
804 {
805 return QQmlListProperty<QDeclarativePluginParameter>(this,
806 0,
807 parameter_append,
808 parameter_count,
809 parameter_at,
810 parameter_clear);
811 }
812
813 /*!
814 \internal
815 */
parameter_append(QQmlListProperty<QDeclarativePluginParameter> * prop,QDeclarativePluginParameter * parameter)816 void QDeclarativePositionSource::parameter_append(QQmlListProperty<QDeclarativePluginParameter> *prop, QDeclarativePluginParameter *parameter)
817 {
818 QDeclarativePositionSource *p = static_cast<QDeclarativePositionSource *>(prop->object);
819 p->m_parameters.append(parameter);
820 }
821
822 /*!
823 \internal
824 */
parameter_count(QQmlListProperty<QDeclarativePluginParameter> * prop)825 int QDeclarativePositionSource::parameter_count(QQmlListProperty<QDeclarativePluginParameter> *prop)
826 {
827 return static_cast<QDeclarativePositionSource *>(prop->object)->m_parameters.count();
828 }
829
830 /*!
831 \internal
832 */
parameter_at(QQmlListProperty<QDeclarativePluginParameter> * prop,int index)833 QDeclarativePluginParameter *QDeclarativePositionSource::parameter_at(QQmlListProperty<QDeclarativePluginParameter> *prop, int index)
834 {
835 return static_cast<QDeclarativePositionSource *>(prop->object)->m_parameters[index];
836 }
837
838 /*!
839 \internal
840 */
parameter_clear(QQmlListProperty<QDeclarativePluginParameter> * prop)841 void QDeclarativePositionSource::parameter_clear(QQmlListProperty<QDeclarativePluginParameter> *prop)
842 {
843 QDeclarativePositionSource *p = static_cast<QDeclarativePositionSource *>(prop->object);
844 p->m_parameters.clear();
845 }
846
847
componentComplete()848 void QDeclarativePositionSource::componentComplete()
849 {
850 m_componentComplete = true;
851 m_parametersInitialized = true;
852 for (QDeclarativePluginParameter *p: qAsConst(m_parameters)) {
853 if (!p->isInitialized()) {
854 m_parametersInitialized = false;
855 connect(p, &QDeclarativePluginParameter::initialized,
856 this, &QDeclarativePositionSource::onParameterInitialized);
857 }
858 }
859
860 if (m_parametersInitialized)
861 tryAttach(m_providerName);
862 }
863
864 /*!
865 \qmlmethod bool QtLocation::PositionSource::setBackendProperty(string name, Variant value)
866
867 Sets the backend-specific property named \a name to \a value.
868 Returns true on success, false otherwise, including if called on an uninitialized PositionSource.
869 Supported backend-specific properties are listed and described in
870 \l {Qt Positioning plugins#Default plugins}.
871
872 \since Qt Positioning 5.14
873
874 \sa backendProperty, QGeoPositionInfoSource::setBackendProperty
875 */
setBackendProperty(const QString & name,const QVariant & value)876 bool QDeclarativePositionSource::setBackendProperty(const QString &name, const QVariant &value)
877 {
878 if (m_positionSource)
879 return m_positionSource->setBackendProperty(name, value);
880 return false;
881 }
882
883 /*!
884 \qmlmethod Variant QtLocation::PositionSource::backendProperty(string name)
885
886 Returns the value of the backend-specific property named \a name, if present.
887 Otherwise, including if called on an uninitialized PositionSource, the return value will be invalid.
888 Supported backend-specific properties are listed and described in
889 \l {Qt Positioning plugins#Default plugins}.
890
891 \since Qt Positioning 5.14
892
893 \sa backendProperty, QGeoPositionInfoSource::setBackendProperty
894 */
backendProperty(const QString & name) const895 QVariant QDeclarativePositionSource::backendProperty(const QString &name) const
896 {
897 if (m_positionSource)
898 return m_positionSource->backendProperty(name);
899 return QVariant();
900 }
901
902 /*!
903 \internal
904 */
sourceErrorReceived(const QGeoPositionInfoSource::Error error)905 void QDeclarativePositionSource::sourceErrorReceived(const QGeoPositionInfoSource::Error error)
906 {
907 if (error == QGeoPositionInfoSource::AccessError)
908 m_sourceError = QDeclarativePositionSource::AccessError;
909 else if (error == QGeoPositionInfoSource::ClosedError)
910 m_sourceError = QDeclarativePositionSource::ClosedError;
911 else if (error == QGeoPositionInfoSource::NoError)
912 return; //nothing to do
913 else
914 m_sourceError = QDeclarativePositionSource::UnknownSourceError;
915
916 emit sourceErrorChanged();
917 }
918
919 QT_END_NAMESPACE
920