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