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_cl.mm
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
6#include <QtCore/QTimerEvent>
7#include <QtCore/QDebug>
8#include <QtCore/qdatetime.h>
9#include <QtCore/qglobal.h>
10#include <QtCore/private/qglobal_p.h>
11#include <QtCore/qtimezone.h>
12#include <QtCore/QPermission>
13#include <QtCore/QCoreApplication>
14
15
16#define MINIMUM_UPDATE_INTERVAL 1000
17
18@interface PositionLocationDelegate : NSObject <CLLocationManagerDelegate>
19@end
20
21@implementation PositionLocationDelegate
22{
23 QGeoPositionInfoSourceCL *m_positionInfoSource;
24}
25
26- (instancetype)initWithInfoSource:(QGeoPositionInfoSourceCL*) positionInfoSource
27{
28 if ((self = [self init])) {
29 m_positionInfoSource = positionInfoSource;
30 }
31 return self;
32}
33
34- (void)locationManagerDidChangeAuthorization: (CLLocationManager *)manager
35{
36 // Since Qt 6.6 the application requests the desired permissions.
37 // This delegate method is invoked always upon CLLocationManager
38 // instantiation, and later if authorization status changes.
39 m_positionInfoSource->changeAuthorizationStatus([manager authorizationStatus]);
40}
41
42- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
43{
44 Q_UNUSED(manager);
45 Q_UNUSED(oldLocation);
46
47 // Convert location timestamp to QDateTime
48 NSTimeInterval locationTimeStamp = [newLocation.timestamp timeIntervalSince1970];
49 const QDateTime timeStamp = QDateTime::fromMSecsSinceEpoch(qRound64(locationTimeStamp * 1000),
50 QTimeZone::UTC);
51
52 // Construct position info from location data
53 QGeoPositionInfo location(QGeoCoordinate(newLocation.coordinate.latitude,
54 newLocation.coordinate.longitude,
55 newLocation.altitude),
56 timeStamp);
57 if (newLocation.horizontalAccuracy >= 0)
58 location.setAttribute(QGeoPositionInfo::HorizontalAccuracy, newLocation.horizontalAccuracy);
59 if (newLocation.verticalAccuracy >= 0)
60 location.setAttribute(QGeoPositionInfo::VerticalAccuracy, newLocation.verticalAccuracy);
61 if (newLocation.course >= 0) {
62 location.setAttribute(QGeoPositionInfo::Direction, newLocation.course);
63 if (__builtin_available(iOS 13.4, macOS 10.15.4, *)) {
64 if (newLocation.courseAccuracy >= 0) {
65 location.setAttribute(QGeoPositionInfo::DirectionAccuracy,
66 newLocation.courseAccuracy);
67 }
68 }
69 }
70 if (newLocation.speed >= 0)
71 location.setAttribute(QGeoPositionInfo::GroundSpeed, newLocation.speed);
72
73 m_positionInfoSource->locationDataAvailable(location);
74}
75
76- (void)locationManager:(CLLocationManager *)manager didFailWithError:(NSError *)error
77{
78 Q_UNUSED(manager);
79 m_positionInfoSource->setError(QGeoPositionInfoSource::AccessError);
80
81 qWarning() << QString::fromNSString([error localizedDescription]);
82
83 if ([error code] == 0
84 && QString::fromNSString([error domain]) == QStringLiteral("kCLErrorDomain"))
85 qWarning() << "(is Wi-Fi turned on?)";
86}
87@end
88
89QT_BEGIN_NAMESPACE
90
91QGeoPositionInfoSourceCL::QGeoPositionInfoSourceCL(QObject *parent)
92 : QGeoPositionInfoSource(parent),
93 m_locationManager(0),
94 m_updatesWanted(false),
95 m_updateTimer(0),
96 m_updateTimeout(0),
97 m_positionError(QGeoPositionInfoSource::NoError)
98{
99}
100
101QGeoPositionInfoSourceCL::~QGeoPositionInfoSourceCL()
102{
103 stopUpdates();
104 [m_locationManager release];
105}
106
107void QGeoPositionInfoSourceCL::setUpdateInterval(int msec)
108{
109 // If msec is 0 we send updates as data becomes available, otherwise we force msec to be equal
110 // to or larger than the minimum update interval.
111 if (msec != 0 && msec < minimumUpdateInterval())
112 msec = minimumUpdateInterval();
113
114 QGeoPositionInfoSource::setUpdateInterval(msec);
115
116 // Must timeout if update takes longer than specified interval
117 m_updateTimeout = msec;
118 if (m_updatesWanted)
119 setTimeoutInterval(m_updateTimeout);
120}
121
122bool QGeoPositionInfoSourceCL::enableLocationManager()
123{
124 if (!m_locationManager) {
125 m_locationManager = [[CLLocationManager alloc] init];
126
127#if defined(Q_OS_IOS)
128 NSDictionary<NSString *, id> *infoDict = [[NSBundle mainBundle] infoDictionary];
129 if (id value = [infoDict objectForKey:@"UIBackgroundModes"]) {
130 if ([value isKindOfClass:[NSArray class]]) {
131 NSArray *modes = static_cast<NSArray *>(value);
132 for (id mode in modes) {
133 if ([@"location" isEqualToString:mode]) {
134 m_locationManager.allowsBackgroundLocationUpdates = YES;
135 break;
136 }
137 }
138 }
139 }
140#endif
141
142 m_locationManager.desiredAccuracy = kCLLocationAccuracyBest;
143 m_locationManager.delegate = [[PositionLocationDelegate alloc] initWithInfoSource:this];
144 }
145
146 return qApp->checkPermission(QLocationPermission{}) == Qt::PermissionStatus::Granted;
147}
148
149void QGeoPositionInfoSourceCL::setTimeoutInterval(int msec)
150{
151 // Start timeout timer
152 if (m_updateTimer) killTimer(m_updateTimer);
153 if (msec > 0) m_updateTimer = startTimer(msec);
154 else m_updateTimer = 0;
155}
156
157void QGeoPositionInfoSourceCL::startUpdates()
158{
159 m_positionError = QGeoPositionInfoSource::NoError;
160 m_updatesWanted = true;
161 if (enableLocationManager()) {
162 [m_locationManager startUpdatingLocation];
163 setTimeoutInterval(m_updateTimeout);
164 } else {
165 setError(QGeoPositionInfoSource::AccessError);
166 }
167}
168
169void QGeoPositionInfoSourceCL::stopUpdates()
170{
171 if (m_locationManager) {
172 [m_locationManager stopUpdatingLocation];
173 m_updatesWanted = false;
174
175 // Stop timeout timer
176 setTimeoutInterval(0);
177 } else {
178 setError(QGeoPositionInfoSource::AccessError);
179 }
180}
181
182void QGeoPositionInfoSourceCL::requestUpdate(int timeout)
183{
184 // Get a single update within timeframe
185 m_positionError = QGeoPositionInfoSource::NoError;
186 if (timeout < minimumUpdateInterval() && timeout != 0)
187 setError(QGeoPositionInfoSource::UpdateTimeoutError);
188 else if (enableLocationManager()) {
189 // This will force LM to generate a new update
190 [m_locationManager stopUpdatingLocation];
191 [m_locationManager startUpdatingLocation];
192 setTimeoutInterval(timeout);
193 } else {
194 setError(QGeoPositionInfoSource::AccessError);
195 }
196}
197
198void QGeoPositionInfoSourceCL::changeAuthorizationStatus(CLAuthorizationStatus status)
199{
200#ifndef Q_OS_MACOS
201 if (status == kCLAuthorizationStatusAuthorizedAlways
202 || status == kCLAuthorizationStatusAuthorizedWhenInUse
203#else
204 if (status == kCLAuthorizationStatusAuthorized
205#endif
206 ) {
207 if (m_updatesWanted)
208 startUpdates();
209 }
210}
211
212void QGeoPositionInfoSourceCL::timerEvent( QTimerEvent * event )
213{
214 // Update timed out?
215 if (event->timerId() == m_updateTimer) {
216 setError(QGeoPositionInfoSource::UpdateTimeoutError);
217
218 // Only timeout once since last data
219 setTimeoutInterval(0);
220
221 // Started for single update?
222 if (!m_updatesWanted)
223 stopUpdates();
224 }
225}
226
227QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceCL::supportedPositioningMethods() const
228{
229 // CoreLocation doesn't say which positioning method(s) it used
230 return QGeoPositionInfoSource::AllPositioningMethods;
231}
232
233int QGeoPositionInfoSourceCL::minimumUpdateInterval() const
234{
236}
237
238void QGeoPositionInfoSourceCL::locationDataAvailable(QGeoPositionInfo location)
239{
240 // Signal position data available
241 m_lastUpdate = location;
242 emit positionUpdated(location);
243
244 // Started for single update?
245 if (!m_updatesWanted)
246 stopUpdates();
247 // ...otherwise restart timeout timer
248 else setTimeoutInterval(m_updateTimeout);
249}
250
251QGeoPositionInfo QGeoPositionInfoSourceCL::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
252{
253 Q_UNUSED(fromSatellitePositioningMethodsOnly);
254
255 return m_lastUpdate;
256}
257
258QGeoPositionInfoSource::Error QGeoPositionInfoSourceCL::error() const
259{
260 return m_positionError;
261}
262
263void QGeoPositionInfoSourceCL::setError(QGeoPositionInfoSource::Error positionError)
264{
265 m_positionError = positionError;
266 if (m_positionError != QGeoPositionInfoSource::NoError)
267 emit QGeoPositionInfoSource::errorOccurred(positionError);
268}
269
270QT_END_NAMESPACE
271
272#include "moc_qgeopositioninfosource_cl_p.cpp"
#define MINIMUM_UPDATE_INTERVAL