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 "qgeopositioninfosource_android_p.h"
41 #include "jnipositioning.h"
42 //#include <QDebug>
43 #include <QGeoPositionInfo>
44
45 #define UPDATE_FROM_COLD_START 2*60*1000
46
47
QGeoPositionInfoSourceAndroid(QObject * parent)48 QGeoPositionInfoSourceAndroid::QGeoPositionInfoSourceAndroid(QObject *parent) :
49 QGeoPositionInfoSource(parent), updatesRunning(false), m_error(NoError), m_requestTimer(this)
50 {
51 androidClassKeyForUpdate = AndroidPositioning::registerPositionInfoSource(this);
52 androidClassKeyForSingleRequest = AndroidPositioning::registerPositionInfoSource(this);
53
54 //qDebug() << "androidClassKey: " << androidClassKeyForUpdate << androidClassKeyForSingleRequest;
55 //by default use all methods
56 setPreferredPositioningMethods(AllPositioningMethods);
57
58 m_requestTimer.setSingleShot(true);
59 QObject::connect(&m_requestTimer, SIGNAL(timeout()), this, SLOT(requestTimeout()));
60 }
61
~QGeoPositionInfoSourceAndroid()62 QGeoPositionInfoSourceAndroid::~QGeoPositionInfoSourceAndroid()
63 {
64 stopUpdates();
65
66 if (m_requestTimer.isActive()) {
67 m_requestTimer.stop();
68 AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest);
69 }
70
71 AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForUpdate);
72 AndroidPositioning::unregisterPositionInfoSource(androidClassKeyForSingleRequest);
73 }
74
setUpdateInterval(int msec)75 void QGeoPositionInfoSourceAndroid::setUpdateInterval(int msec)
76 {
77 int previousInterval = updateInterval();
78 msec = (((msec > 0) && (msec < minimumUpdateInterval())) || msec < 0)? minimumUpdateInterval() : msec;
79
80 if (msec == previousInterval)
81 return;
82
83 QGeoPositionInfoSource::setUpdateInterval(msec);
84
85 if (updatesRunning)
86 reconfigureRunningSystem();
87 }
88
lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const89 QGeoPositionInfo QGeoPositionInfoSourceAndroid::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
90 {
91 return AndroidPositioning::lastKnownPosition(fromSatellitePositioningMethodsOnly);
92 }
93
supportedPositioningMethods() const94 QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceAndroid::supportedPositioningMethods() const
95 {
96 return AndroidPositioning::availableProviders();
97 }
98
setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)99 void QGeoPositionInfoSourceAndroid::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)
100 {
101 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
102 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
103 if (previousPreferredPositioningMethods == preferredPositioningMethods())
104 return;
105
106 if (updatesRunning)
107 reconfigureRunningSystem();
108 }
109
minimumUpdateInterval() const110 int QGeoPositionInfoSourceAndroid::minimumUpdateInterval() const
111 {
112 return 50;
113 }
114
error() const115 QGeoPositionInfoSource::Error QGeoPositionInfoSourceAndroid::error() const
116 {
117 return m_error;
118 }
119
setError(Error error)120 void QGeoPositionInfoSourceAndroid::setError(Error error)
121 {
122 // qDebug() << "setError: " << error;
123 if (error != QGeoPositionInfoSource::NoError)
124 {
125 m_error = error;
126 emit QGeoPositionInfoSource::error(m_error);
127 }
128 }
129
startUpdates()130 void QGeoPositionInfoSourceAndroid::startUpdates()
131 {
132 if (updatesRunning)
133 return;
134
135 if (preferredPositioningMethods() == 0) {
136 setError(UnknownSourceError);
137 return;
138 }
139
140 updatesRunning = true;
141 QGeoPositionInfoSource::Error error = AndroidPositioning::startUpdates(androidClassKeyForUpdate);
142 if (error != QGeoPositionInfoSource::NoError)
143 updatesRunning = false;
144
145 setError(error);
146 }
147
stopUpdates()148 void QGeoPositionInfoSourceAndroid::stopUpdates()
149 {
150 if (!updatesRunning)
151 return;
152
153 updatesRunning = false;
154 AndroidPositioning::stopUpdates(androidClassKeyForUpdate);
155 }
156
requestUpdate(int timeout)157 void QGeoPositionInfoSourceAndroid::requestUpdate(int timeout)
158 {
159 if (m_requestTimer.isActive())
160 return;
161
162 if (timeout != 0 && timeout < minimumUpdateInterval()) {
163 emit updateTimeout();
164 return;
165 }
166
167 if (timeout == 0)
168 timeout = UPDATE_FROM_COLD_START;
169
170 m_requestTimer.start(timeout);
171
172 // if updates already running with interval equal to timeout
173 // then we wait for next update coming through
174 // assume that a single update will not be quicker than regular updates anyway
175 if (updatesRunning && updateInterval() <= timeout)
176 return;
177
178 QGeoPositionInfoSource::Error error = AndroidPositioning::requestUpdate(androidClassKeyForSingleRequest);
179 if (error != QGeoPositionInfoSource::NoError)
180 m_requestTimer.stop();
181
182 setError(error);
183 }
184
processPositionUpdate(const QGeoPositionInfo & pInfo)185 void QGeoPositionInfoSourceAndroid::processPositionUpdate(const QGeoPositionInfo &pInfo)
186 {
187 //single update request and served as part of regular update
188 if (m_requestTimer.isActive())
189 m_requestTimer.stop();
190
191 emit positionUpdated(pInfo);
192 }
193
194 // Might still be called multiple times (once for each provider)
processSinglePositionUpdate(const QGeoPositionInfo & pInfo)195 void QGeoPositionInfoSourceAndroid::processSinglePositionUpdate(const QGeoPositionInfo &pInfo)
196 {
197 //timeout but we received a late update -> ignore
198 if (!m_requestTimer.isActive())
199 return;
200
201 queuedSingleUpdates.append(pInfo);
202 }
203
locationProviderDisabled()204 void QGeoPositionInfoSourceAndroid::locationProviderDisabled()
205 {
206 setError(QGeoPositionInfoSource::ClosedError);
207 }
208
locationProvidersChanged()209 void QGeoPositionInfoSourceAndroid::locationProvidersChanged()
210 {
211 emit supportedPositioningMethodsChanged();
212 }
213
requestTimeout()214 void QGeoPositionInfoSourceAndroid::requestTimeout()
215 {
216 AndroidPositioning::stopUpdates(androidClassKeyForSingleRequest);
217 //no queued update to process -> timeout
218 const int count = queuedSingleUpdates.count();
219
220 if (!count) {
221 emit updateTimeout();
222 return;
223 }
224
225 //pick best
226 QGeoPositionInfo best = queuedSingleUpdates[0];
227 for (int i = 1; i < count; i++) {
228 const QGeoPositionInfo info = queuedSingleUpdates[i];
229
230 //anything newer by 20s is always better
231 const qint64 timeDelta = best.timestamp().secsTo(info.timestamp());
232 if (abs(timeDelta) > 20) {
233 if (timeDelta > 0)
234 best = info;
235 continue;
236 }
237
238 //compare accuracy
239 if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy) &&
240 best.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
241 {
242 best = info.attribute(QGeoPositionInfo::HorizontalAccuracy) <
243 best.attribute(QGeoPositionInfo::HorizontalAccuracy) ? info : best;
244 continue;
245 }
246
247 //prefer info with accuracy information
248 if (info.hasAttribute(QGeoPositionInfo::HorizontalAccuracy))
249 best = info;
250 }
251
252 queuedSingleUpdates.clear();
253 emit positionUpdated(best);
254 }
255
256 /*
257 Updates the system assuming that updateInterval
258 and/or preferredPositioningMethod have changed.
259 */
reconfigureRunningSystem()260 void QGeoPositionInfoSourceAndroid::reconfigureRunningSystem()
261 {
262 if (!updatesRunning)
263 return;
264
265 stopUpdates();
266 startUpdates();
267 }
268