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
qgeopositioninfosource_android.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 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
6#include <QGeoPositionInfo>
7#include <QVariantMap>
8
9using namespace Qt::StringLiterals;
10
11static constexpr int kUpdateFromColdStart = 2 * 60 * 1000;
12static constexpr int kRegularUpdatesTimerInterval = 30 * 1000;
13
14static constexpr auto kUseAltitudeConverter = "useMslAltitude"_L1;
15
16QGeoPositionInfoSourceAndroid::QGeoPositionInfoSourceAndroid(const QVariantMap &parameters,
17 QObject *parent) :
18 QGeoPositionInfoSource(parent)
19{
20 androidClassKeyForUpdate = AndroidPositioning::registerPositionInfoSource(this);
21 androidClassKeyForSingleRequest = AndroidPositioning::registerPositionInfoSource(this);
22
23 parseParameters(parameters);
24
25 //by default use all methods
26 setPreferredPositioningMethods(AllPositioningMethods);
27
28 m_requestTimer.setSingleShot(true);
29 connect(&m_requestTimer, &QTimer::timeout, this,
30 &QGeoPositionInfoSourceAndroid::requestTimeout);
31
32 m_regularUpdatesTimer.setSingleShot(false);
33 connect(&m_regularUpdatesTimer, &QTimer::timeout, this,
34 &QGeoPositionInfoSourceAndroid::regularUpdatesTimeout);
35}
36
38{
40
41 if (m_requestTimer.isActive()) {
42 m_requestTimer.stop();
43 AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest);
44 }
45
46 AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForUpdate);
47 AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForSingleRequest);
48}
49
51{
52 int previousInterval = updateInterval();
53 msec = (((msec > 0) && (msec < minimumUpdateInterval())) || msec < 0)? minimumUpdateInterval() : msec;
54
55 if (msec == previousInterval)
56 return;
57
58 QGeoPositionInfoSource::setUpdateInterval(msec);
59
60 if (updatesRunning)
61 reconfigureRunningSystem();
62}
63
64QGeoPositionInfo QGeoPositionInfoSourceAndroid::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
65{
66 return AndroidPositioning::lastKnownPosition(fromSatellitePositioningMethodsOnly,
68}
69
71{
72 return AndroidPositioning::availableProviders();
73}
74
75void QGeoPositionInfoSourceAndroid::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)
76{
77 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
78 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
79 if (previousPreferredPositioningMethods == preferredPositioningMethods())
80 return;
81
82 if (updatesRunning)
83 reconfigureRunningSystem();
84}
85
87{
88 return 50;
89}
90
95
97{
98 return m_useAltitudeConverter;
99}
100
101void QGeoPositionInfoSourceAndroid::setError(Error error)
102{
103 m_error = error;
104 if (error != QGeoPositionInfoSource::NoError)
105 emit QGeoPositionInfoSource::errorOccurred(m_error);
106}
107
108void QGeoPositionInfoSourceAndroid::parseParameters(const QVariantMap &parameters)
109{
110 m_useAltitudeConverter = parameters.value(kUseAltitudeConverter, false).toBool();
111}
112
114{
115 if (updatesRunning)
116 return;
117
118 m_error = QGeoPositionInfoSource::NoError;
119
120 if (preferredPositioningMethods() == 0) {
121 setError(UnknownSourceError);
122 return;
123 }
124
125 updatesRunning = true;
126 // Start calculating updates from now.
127 m_lastUpdateTime = QDateTime::currentMSecsSinceEpoch();
128 m_regularUpdatesErrorRaised = false;
129 QGeoPositionInfoSource::Error error = AndroidPositioning::startUpdates(androidClassKeyForUpdate);
130 if (error != QGeoPositionInfoSource::NoError) {
131 updatesRunning = false;
132 setError(error);
133 } else {
134 m_regularUpdatesTimer.start(kRegularUpdatesTimerInterval);
135 }
136}
137
139{
140 if (!updatesRunning)
141 return;
142
143 updatesRunning = false;
144 m_regularUpdatesTimer.stop();
145 AndroidPositioning::stopUpdates(androidClassKeyForUpdate);
146}
147
149{
150 if (m_requestTimer.isActive())
151 return;
152
153 m_error = QGeoPositionInfoSource::NoError;
154
155 if (timeout != 0 && timeout < minimumUpdateInterval()) {
156 setError(QGeoPositionInfoSource::UpdateTimeoutError);
157 return;
158 }
159
160 if (timeout == 0)
161 timeout = kUpdateFromColdStart;
162
163 m_requestTimer.start(timeout);
164
165 // if updates already running with interval equal to timeout
166 // then we wait for next update coming through
167 // assume that a single update will not be quicker than regular updates anyway
168 if (updatesRunning && updateInterval() <= timeout)
169 return;
170
171 const QGeoPositionInfoSource::Error error =
172 AndroidPositioning::requestUpdate(androidClassKeyForSingleRequest, timeout);
173 if (error != QGeoPositionInfoSource::NoError) {
174 m_requestTimer.stop();
175 setError(error);
176 }
177}
178
179void QGeoPositionInfoSourceAndroid::processPositionUpdate(const QGeoPositionInfo &pInfo)
180{
181 //single update request and served as part of regular update
182 if (m_requestTimer.isActive())
183 m_requestTimer.stop();
184
185 m_lastUpdateTime = QDateTime::currentMSecsSinceEpoch();
186 m_regularUpdatesErrorRaised = false;
187
188 emit positionUpdated(pInfo);
189}
190
191// Might still be called multiple times (once for each provider)
193{
194 //timeout but we received a late update -> ignore
195 if (!m_requestTimer.isActive())
196 return;
197
198 queuedSingleUpdates.append(pInfo);
199 // Calculate the maximum amount of possibly received updates. It depends on
200 // preferred positioning methods. Two updates if we have both Satellite and
201 // Network, and only one otherwise.
202 const qsizetype maxPossibleUpdates =
203 (preferredPositioningMethods() == QGeoPositionInfoSource::AllPositioningMethods)
204 ? 2 : 1;
205 // If we get the maximum number of updates, we do not need to wait for more
206 if (queuedSingleUpdates.size() == maxPossibleUpdates) {
207 m_requestTimer.stop();
208 requestTimeout();
209 }
210}
211
213{
214 if (updatesRunning && !m_regularUpdatesErrorRaised) {
215 m_regularUpdatesErrorRaised = true;
216 setError(QGeoPositionInfoSource::UpdateTimeoutError);
217 }
218
219 setError(QGeoPositionInfoSource::ClosedError);
220}
221
223{
224 emit supportedPositioningMethodsChanged();
225}
226
227void QGeoPositionInfoSourceAndroid::requestTimeout()
228{
229 AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest);
230 //no queued update to process -> timeout
231
232 if (queuedSingleUpdates.isEmpty()) {
233 setError(QGeoPositionInfoSource::UpdateTimeoutError);
234 return;
235 }
236
237 auto byAccuracy = [](const QGeoPositionInfo &info, const QGeoPositionInfo &best) {
238 //anything newer by 20s is always better
239 const qint64 timeDelta = best.timestamp().secsTo(info.timestamp());
240 if (abs(timeDelta) > 20)
241 return timeDelta > 0;
242
243 //compare accuracy
244 if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy) &&
245 best.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
246 {
247 return info.attribute(QGeoPositionInfo::HorizontalAccuracy) <
248 best.attribute(QGeoPositionInfo::HorizontalAccuracy);
249 }
250
251 //prefer info with accuracy information
252 if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
253 return true;
254
255 return false;
256 };
257
258 QGeoPositionInfo best = *std::min_element(queuedSingleUpdates.begin(),
259 queuedSingleUpdates.end(), byAccuracy);
260 queuedSingleUpdates.clear();
261 emit positionUpdated(best);
262}
263
264void QGeoPositionInfoSourceAndroid::regularUpdatesTimeout()
265{
266 if (!m_regularUpdatesErrorRaised) {
267 const auto now = QDateTime::currentMSecsSinceEpoch();
268 if ((now - m_lastUpdateTime) > (updateInterval() + kUpdateFromColdStart)) {
269 m_regularUpdatesErrorRaised = true;
270 setError(QGeoPositionInfoSource::UpdateTimeoutError);
271 }
272 }
273}
274
275/*
276 Updates the system assuming that updateInterval
277 and/or preferredPositioningMethod have changed.
278 */
279void QGeoPositionInfoSourceAndroid::reconfigureRunningSystem()
280{
281 if (!updatesRunning)
282 return;
283
285 startUpdates();
286}
QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly=false) const override
Returns an update containing the last known position, or a null update if none is available.
void processSinglePositionUpdate(const QGeoPositionInfo &pInfo)
virtual void requestUpdate(int timeout=0) override
void processPositionUpdate(const QGeoPositionInfo &pInfo)
void setPreferredPositioningMethods(PositioningMethods methods) override
Error error() const override
Returns the type of error that last occurred.
PositioningMethods supportedPositioningMethods() const override
Returns the positioning methods available to this source.
void stopUpdates(int androidClassKey)
static constexpr auto kUseAltitudeConverter
static constexpr int kRegularUpdatesTimerInterval
static constexpr int kUpdateFromColdStart