60023d67e505047e1d11cfd8e43ce25b76424f66
[googlelatitude] / liblocationmaemo5 / qgeopositioninfosource_maemo5.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
6 **
7 ** This file is part of the Qt Mobility Components.
8 **
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 **
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 **
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
29 **
30 ** Other Usage
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qgeopositioninfosource_maemo5_p.h"
43 #include "liblocationwrapper_p.h"
44 #include <qnumeric.h>
45
46 using namespace std;
47
48 QTM_BEGIN_NAMESPACE
49
50 QGeoPositionInfoSourceMaemo::QGeoPositionInfoSourceMaemo(QObject *parent)
51         : QGeoPositionInfoSource(parent)
52 {
53     qDebug() << "* QGeoPositionInfoSourceMaemo Fremantle Backport";
54     // default values
55     timerInterval = DEFAULT_UPDATE_INTERVAL;
56     updateTimer = new QTimer(this);
57     updateTimer->setSingleShot(true);
58     connect(updateTimer, SIGNAL(timeout()), this, SLOT(updateTimeoutElapsed()));
59
60     requestTimer = new QTimer(this);
61     requestTimer->setSingleShot(true);
62     connect(requestTimer, SIGNAL(timeout()), this, SLOT(requestTimeoutElapsed()));
63
64     connect(LiblocationWrapper::instance(), SIGNAL(positionUpdated(QGeoPositionInfo)), this, SLOT(newPositionUpdate(QGeoPositionInfo)));
65
66     errorOccurred = false;
67     errorSent = false;
68
69     positionInfoState = QGeoPositionInfoSourceMaemo::Undefined;
70 }
71
72 int QGeoPositionInfoSourceMaemo::init()
73 {
74     if (LiblocationWrapper::instance()->inited()) {
75         connect(LiblocationWrapper::instance(), SIGNAL(error()), this, SLOT(error()));
76         return INIT_OK;
77     } else {
78         return INIT_FAILED;
79     }
80 }
81
82 QGeoPositionInfo QGeoPositionInfoSourceMaemo::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
83 {
84     return (LiblocationWrapper::instance()->lastKnownPosition(fromSatellitePositioningMethodsOnly));
85 }
86
87 QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceMaemo::supportedPositioningMethods() const
88 {
89     QGeoPositionInfoSource::PositioningMethods methods;
90
91     if (!GConfItem("/system/nokia/location/gps-disabled").value().toBool())
92         methods |= SatellitePositioningMethods;
93     if (!GConfItem("/system/nokia/location/network-disabled").value().toBool())
94         methods |= NonSatellitePositioningMethods;
95     if (methods.testFlag(SatellitePositioningMethods) && methods.testFlag(NonSatellitePositioningMethods))
96         methods |= AllPositioningMethods;
97
98     return methods;
99 }
100
101 void QGeoPositionInfoSourceMaemo::setUpdateInterval(int msec)
102 {
103     bool updateTimerInterval = false;
104
105     if (positionInfoState & QGeoPositionInfoSourceMaemo::PowersaveActive)
106         if (positionInfoState & QGeoPositionInfoSourceMaemo::Stopped)
107             updateTimerInterval = true;
108
109     if (!msec) {
110         timerInterval = MINIMUM_UPDATE_INTERVAL;
111         QGeoPositionInfoSource::setUpdateInterval(0);
112     } else {
113         timerInterval = (msec < MINIMUM_UPDATE_INTERVAL) ? MINIMUM_UPDATE_INTERVAL : msec;
114         QGeoPositionInfoSource::setUpdateInterval(timerInterval);
115     }
116
117     if (timerInterval >= POWERSAVE_THRESHOLD)
118         positionInfoState |= QGeoPositionInfoSourceMaemo::PowersaveActive;
119     else
120         positionInfoState &= ~QGeoPositionInfoSourceMaemo::PowersaveActive;
121
122     // If powersave has been active when new update interval has been set,
123     // ensure that timer is started.
124     if (updateTimerInterval)
125         startLocationDaemon();
126
127     // Ensure that new timer interval is taken into use immediately.
128     activateTimer();
129 }
130
131 void QGeoPositionInfoSourceMaemo::setPreferredPositioningMethods(PositioningMethods methods)
132 {
133     QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
134 }
135
136 int QGeoPositionInfoSourceMaemo::minimumUpdateInterval() const
137 {
138     return MINIMUM_UPDATE_INTERVAL;
139 }
140
141 // public slots:
142 void QGeoPositionInfoSourceMaemo::startUpdates()
143 {
144     startLocationDaemon();
145
146     // Ensure that powersave is selected, if stopUpdates() has been called,
147     // but selected update interval is still greater than POWERSAVE_THRESHOLD.
148     if (timerInterval >= POWERSAVE_THRESHOLD)
149         positionInfoState |= QGeoPositionInfoSourceMaemo::PowersaveActive;
150
151     activateTimer();
152 }
153
154 void QGeoPositionInfoSourceMaemo::stopUpdates()
155 {
156     positionInfoState &= ~QGeoPositionInfoSourceMaemo::PowersaveActive;
157
158     if (!(positionInfoState & QGeoPositionInfoSourceMaemo::RequestActive)) {
159         updateTimer->stop();
160         if (LiblocationWrapper::instance()->isActive())
161             LiblocationWrapper::instance()->stop();
162     }
163
164     errorOccurred = false;
165     errorSent = false;
166
167     positionInfoState &= ~QGeoPositionInfoSourceMaemo::Started;
168     positionInfoState |= QGeoPositionInfoSourceMaemo::Stopped;
169 }
170
171 void QGeoPositionInfoSourceMaemo::requestUpdate(int timeout)
172 {
173     int timeoutForRequest = 0;
174
175     if (!timeout) {
176         if (LiblocationWrapper::instance()->isActive())
177             // If GPS is active, assume quick fix.
178             timeoutForRequest = DEFAULT_UPDATE_INTERVAL;
179         else
180             // Otherwise reserve longer time to get a fix.
181             timeoutForRequest = POWERSAVE_POWERON_PERIOD;
182     } else if (timeout < MINIMUM_UPDATE_INTERVAL) {
183         if (positionInfoState & QGeoPositionInfoSourceMaemo::RequestActive)
184             return;
185
186         emit updateTimeout();
187         return;
188     } else {
189         timeoutForRequest = timeout;
190     }
191
192     positionInfoState |= QGeoPositionInfoSourceMaemo::RequestActive;
193
194     if (!(LiblocationWrapper::instance()->isActive()))
195         LiblocationWrapper::instance()->start();
196
197     activateTimer();
198     requestTimer->start(timeoutForRequest);
199 }
200
201 void QGeoPositionInfoSourceMaemo::newPositionUpdate(const QGeoPositionInfo &position)
202 {    
203     /*
204         Invalid fixes have NaN for horizontal accuracy regardless of
205         whether they come from satellite or non-satellite position methods.
206
207         Satellite fixes always have LOCATION_GPS_DEVICE_TIME_SET.
208         If this is not set and we have a numeric value for horizontal
209         accuracy then we are dealing with a non-satellite based positioning
210         method.
211
212         Since QGeoPositionInfo instances are only considered valid if
213         they have a valid coordinate and a valid timestamp, we use
214         the current date and time as the timestamp for the network based
215         positioning.  This will help in the case where someone wants to
216         reply a journey from a log file.
217
218         Based on some logging it looks like satellite and non-satellite
219         methods can be distinguished (after the initial fix) by whether
220         the time has been set and / or whether the horizontal accuracy
221         is above or below around 500 metres.  Using the timestamp
222         appears to be more definitive than using the accuracy.
223     */
224
225     const bool horizontalAccuracyDefined = !qIsNaN(position.attribute(QGeoPositionInfo::HorizontalAccuracy));
226     const bool hasTimeStamp = !position.timestamp().isNull();
227
228     if (horizontalAccuracyDefined) {
229         if (hasTimeStamp) {
230             //Valid satellite fix
231             lastUpdateFromSatellite = position;
232         } else {
233             //Valid non-satellite fix
234             QGeoPositionInfo networkPosition(position);
235             networkPosition.setTimestamp(QDateTime::currentDateTime());
236             lastUpdateFromNetwork = networkPosition;
237         }
238     } else {
239         //Invalid position update
240         if (hasTimeStamp) {
241             lastUpdateFromSatellite = QGeoPositionInfo();
242         } else {
243             lastUpdateFromNetwork = QGeoPositionInfo();
244         }
245     }
246 }
247
248 void QGeoPositionInfoSourceMaemo::updateTimeoutElapsed()
249 {
250     QGeoPositionInfo position;
251
252     QGeoPositionInfoSource::PositioningMethods methods = preferredPositioningMethods();
253
254     if (methods.testFlag(AllPositioningMethods)) {
255         methods |= SatellitePositioningMethods;
256         methods |= NonSatellitePositioningMethods;
257     }
258
259     if (methods.testFlag(SatellitePositioningMethods) && !methods.testFlag(NonSatellitePositioningMethods)) {
260         //only SatellitePositioningMethods preferred
261         position = lastUpdateFromSatellite;
262     } else if (methods.testFlag(NonSatellitePositioningMethods) && !methods.testFlag(SatellitePositioningMethods)) {
263         //only NonSatellitePositioningMethods preferred
264         position = lastUpdateFromNetwork;
265     } else {
266         //AllPositioningMethods or none preferred
267         if (lastUpdateFromSatellite.isValid())
268             position = lastUpdateFromSatellite;
269         else
270             position = lastUpdateFromNetwork;
271     }
272
273     if (position.isValid()) {
274         errorOccurred = false;
275         errorSent = false;
276
277         if (positionInfoState & QGeoPositionInfoSourceMaemo::RequestActive) {
278             positionInfoState &= ~QGeoPositionInfoSourceMaemo::RequestActive;
279             requestTimer->stop();
280
281             if (positionInfoState & QGeoPositionInfoSourceMaemo::Stopped)
282                 if (LiblocationWrapper::instance()->isActive())
283                     LiblocationWrapper::instance()->stop();
284
285             // Ensure that requested position fix is emitted even though
286             // powersave is active and GPS would normally be off.
287             if ((positionInfoState & QGeoPositionInfoSourceMaemo::PowersaveActive) &&
288                (positionInfoState & QGeoPositionInfoSourceMaemo::Stopped)) {
289                 emit positionUpdated(position);
290             }
291         }
292
293         // Make sure that if update is triggered when waking up, there
294         // is no false position update.
295         if (!((positionInfoState & QGeoPositionInfoSourceMaemo::PowersaveActive) &&
296              (positionInfoState & QGeoPositionInfoSourceMaemo::Stopped)))
297             emit positionUpdated(position);
298     } else {
299         // if an error occurs when we are updating periodically and we haven't
300         // sent an error since the last fix...
301         if (!(positionInfoState & QGeoPositionInfoSourceMaemo::RequestActive) &&
302                 errorOccurred && !errorSent) {
303             errorSent = true;
304             // we need to emit the updateTimeout signal
305             emit updateTimeout();
306         }
307     }
308     activateTimer();
309 }
310
311 void QGeoPositionInfoSourceMaemo::requestTimeoutElapsed()
312 {
313     updateTimer->stop();
314     emit updateTimeout();
315
316     positionInfoState &= ~QGeoPositionInfoSourceMaemo::RequestActive;
317
318     if (positionInfoState & QGeoPositionInfoSourceMaemo::Stopped)
319         if (LiblocationWrapper::instance()->isActive())
320             LiblocationWrapper::instance()->stop();
321
322     activateTimer();
323 }
324
325 void QGeoPositionInfoSourceMaemo::error()
326 {
327     errorOccurred = true;
328 }
329
330 void QGeoPositionInfoSourceMaemo::activateTimer()
331 {
332     if (positionInfoState & QGeoPositionInfoSourceMaemo::RequestActive) {
333         updateTimer->start(MINIMUM_UPDATE_INTERVAL);
334         return;
335     }
336
337     if (positionInfoState & QGeoPositionInfoSourceMaemo::PowersaveActive) {
338         if (positionInfoState & QGeoPositionInfoSourceMaemo::Started) {
339             // Cannot call stopUpdates() here since we want to keep powersave
340             // active.
341             if (LiblocationWrapper::instance()->isActive())
342                 LiblocationWrapper::instance()->stop();
343             updateTimer->start(timerInterval - POWERSAVE_POWERON_PERIOD);
344             errorOccurred = false;
345             errorSent = false;
346
347             positionInfoState &= ~QGeoPositionInfoSourceMaemo::Started;
348             positionInfoState |= QGeoPositionInfoSourceMaemo::Stopped;
349         } else if (positionInfoState & QGeoPositionInfoSourceMaemo::Stopped) {
350             startLocationDaemon();
351             updateTimer->start(POWERSAVE_POWERON_PERIOD);
352         }
353         return;
354     }
355
356     if (positionInfoState & QGeoPositionInfoSourceMaemo::Started)
357         updateTimer->start(timerInterval);
358 }
359
360 void QGeoPositionInfoSourceMaemo::startLocationDaemon()
361 {
362     if (!(LiblocationWrapper::instance()->isActive()))
363         LiblocationWrapper::instance()->start();
364     positionInfoState |= QGeoPositionInfoSourceMaemo::Started;
365     positionInfoState &= ~QGeoPositionInfoSourceMaemo::Stopped;
366 }
367
368 #include "moc_qgeopositioninfosource_maemo5_p.cpp"
369 QTM_END_NAMESPACE
370