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