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