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