Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qdarwinpermissionplugin_location.mm
Go to the documentation of this file.
1// Copyright (C) 2022 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
6#include <deque>
7
8#include <CoreLocation/CoreLocation.h>
9
10@interface QDarwinLocationPermissionHandler () <CLLocationManagerDelegate>
11@property (nonatomic, retain) CLLocationManager *manager;
12@end
13
14Q_LOGGING_CATEGORY(lcLocationPermission, "qt.permissions.location");
15
17{
18 // After creating a CLLocationManager the authorizationStatus
19 // will initially be kCLAuthorizationStatusNotDetermined. The
20 // status will then update to an actual status if the app was
21 // previously authorized/denied once the location services
22 // do some initial book-keeping in the background. By kicking
23 // off a CLLocationManager early on here, we ensure that by
24 // the time the user calls checkPermission the authorization
25 // status has been resolved.
26 qCDebug(lcLocationPermission) << "Warming up location services";
27 [[CLLocationManager new] release];
28}
29
31
37
39 std::deque<PermissionRequest> m_requests;
40}
41
42- (instancetype)init
43{
44 if ((self = [super init])) {
45 // The delegate callbacks will come in on the thread that
46 // the CLLocationManager is created on, and we want those
47 // to come in on the main thread, so we defer creation
48 // of the manger until requestPermission, where we know
49 // we are on the main thread.
50 self.manager = nil;
51 }
52
53 return self;
54}
55
56- (Qt::PermissionStatus)checkPermission:(QPermission)permission
57{
58 const auto locationPermission = *permission.value<QLocationPermission>();
59
60 auto status = [self authorizationStatus:locationPermission];
62 return status;
63
64 return [self accuracyAuthorization:locationPermission];
65}
66
67- (Qt::PermissionStatus)authorizationStatus:(QLocationPermission)permission
68{
69 NSString *bundleIdentifier = NSBundle.mainBundle.bundleIdentifier;
70 if (!bundleIdentifier || !bundleIdentifier.length) {
71 qCWarning(lcLocationPermission) << "Missing bundle identifier"
72 << "in Info.plist. Can not use location permissions.";
74 }
75
76#if defined(Q_OS_VISIONOS)
77 if (permission.availability() == QLocationPermission::Always)
79#endif
80
81 auto status = [self authorizationStatus];
82 switch (status) {
83 case kCLAuthorizationStatusRestricted:
84 case kCLAuthorizationStatusDenied:
86 case kCLAuthorizationStatusNotDetermined:
88#if !defined(Q_OS_VISIONOS)
89 case kCLAuthorizationStatusAuthorizedAlways:
91#endif
92#if defined(Q_OS_IOS) || defined(Q_OS_VISIONOS)
93 case kCLAuthorizationStatusAuthorizedWhenInUse:
94 if (permission.availability() == QLocationPermission::Always)
97#endif
98 }
99
100 qCWarning(lcPermissions) << "Unknown permission status" << status << "detected in" << self;
102}
103
104- (CLAuthorizationStatus)authorizationStatus
105{
106 if (self.manager) {
107 if (@available(macOS 11, iOS 14, *))
108 return self.manager.authorizationStatus;
109 }
110
111 return QT_IGNORE_DEPRECATIONS(CLLocationManager.authorizationStatus);
112}
113
114- (Qt::PermissionStatus)accuracyAuthorization:(QLocationPermission)permission
115{
116 auto status = CLAccuracyAuthorizationReducedAccuracy;
117 if (@available(macOS 11, iOS 14, *))
118 status = self.manager.accuracyAuthorization;
119
120 switch (status) {
121 case CLAccuracyAuthorizationFullAccuracy:
123 case CLAccuracyAuthorizationReducedAccuracy:
124 if (permission.accuracy() == QLocationPermission::Approximate)
126 else
128 }
129
130 qCWarning(lcPermissions) << "Unknown accuracy status" << status << "detected in" << self;
132}
133
134- (QStringList)usageDescriptionsFor:(QPermission)permission
135{
136#if defined(Q_OS_MACOS)
137 return { "NSLocationUsageDescription" };
138#else // iOS 11 and above
139 QStringList usageDescriptions = { "NSLocationWhenInUseUsageDescription" };
140 const auto locationPermission = *permission.value<QLocationPermission>();
141 if (locationPermission.availability() == QLocationPermission::Always)
142 usageDescriptions << "NSLocationAlwaysAndWhenInUseUsageDescription";
143 return usageDescriptions;
144#endif
145}
146
147- (void)requestPermission:(QPermission)permission withCallback:(PermissionCallback)callback
148{
149 const bool requestAlreadyInFlight = !m_requests.empty();
150
151 m_requests.push_back({ permission, callback });
152
153 if (requestAlreadyInFlight) {
154 qCDebug(lcLocationPermission).nospace() << "Already processing "
155 << m_requests.front().permission << ". Deferring request";
156 } else {
157 [self requestQueuedPermission];
158 }
159}
160
161- (void)requestQueuedPermission
162{
163 Q_ASSERT(!m_requests.empty());
164 const auto permission = m_requests.front().permission;
165
166 qCDebug(lcLocationPermission) << "Requesting" << permission;
167
168 if (!self.manager) {
169 self.manager = [[CLLocationManager new] autorelease];
170 self.manager.delegate = self;
171 }
172
173 const auto locationPermission = *permission.value<QLocationPermission>();
174 switch (locationPermission.availability()) {
176 // The documentation specifies that requestWhenInUseAuthorization can
177 // only be called when the current authorization status is undetermined.
178 switch ([self authorizationStatus]) {
179 case kCLAuthorizationStatusNotDetermined:
180 [self.manager requestWhenInUseAuthorization];
181 break;
182 default:
183 [self deliverResult];
184 }
185 break;
187#if defined(Q_OS_VISIONOS)
188 [self deliverResult]; // Not supported
189#else
190 // The documentation specifies that requestAlwaysAuthorization can only
191 // be called when the current authorization status is either undetermined,
192 // or authorized when in use.
193 switch ([self authorizationStatus]) {
194 case kCLAuthorizationStatusNotDetermined:
195 [self.manager requestAlwaysAuthorization];
196 break;
197#ifdef Q_OS_IOS
198 case kCLAuthorizationStatusAuthorizedWhenInUse:
199 // Unfortunately when asking for AlwaysAuthorization when in
200 // the WhenInUse state, to "upgrade" the permission, the iOS
201 // location system will not give us a callback if the user
202 // denies the request, leaving us hanging without a way to
203 // respond to the permission request.
204 qCWarning(lcLocationPermission) << "QLocationPermission::WhenInUse"
205 << "can not be upgraded to QLocationPermission::Always on iOS."
206 << "Please request QLocationPermission::Always directly.";
208#endif
209 default:
210 [self deliverResult];
211 }
212#endif
213 break;
214 }
215}
216
217- (void)locationManager:(CLLocationManager *)manager didChangeAuthorizationStatus:(CLAuthorizationStatus)status
218{
219 qCDebug(lcLocationPermission) << "Processing authorization"
220 << "update with status" << status;
221
222 if (m_requests.empty()) {
223 qCDebug(lcLocationPermission) << "No requests in flight. Ignoring.";
224 return;
225 }
226
227 if (status == kCLAuthorizationStatusNotDetermined) {
228 // Initializing a CLLocationManager will result in an initial
229 // callback to the delegate even before we've requested any
230 // location permissions. Normally we would ignore this callback
231 // due to the request queue check above, but if this callback
232 // comes in after the application has requested a permission
233 // we don't want to report the undetermined status, but rather
234 // wait for the actual result to come in.
235 qCDebug(lcLocationPermission) << "Ignoring delegate callback"
236 << "with status kCLAuthorizationStatusNotDetermined";
237 return;
238 }
239
240 [self deliverResult];
241}
242
243- (void)deliverResult
244{
245 auto request = m_requests.front();
246 m_requests.pop_front();
247
248 auto status = [self checkPermission:request.permission];
249 qCDebug(lcLocationPermission) << "Result for"
250 << request.permission << "was" << status;
251
252 request.callback(status);
253
254 if (!m_requests.empty()) {
255 qCDebug(lcLocationPermission) << "Still have"
256 << m_requests.size() << "deferred request(s)";
257 [self requestQueuedPermission];
258 }
259}
260
261@end
262
263#include "moc_qdarwinpermissionplugin_p_p.cpp"
Access the user's location.
Q_CORE_EXPORT Accuracy accuracy() const
Returns the accuracy of the request.
\inmodule QtCore \inheaderfile QPermissions
std::optional< T > value() const
\inmodule QtCore
void requestPermission(const QPermission &permission, const PermissionCallback &callback)
std::function< void(Qt::PermissionStatus)> PermissionCallback
Definition qcompare.h:63
QString self
Definition language.cpp:58
#define Q_FALLTHROUGH()
#define QT_IGNORE_DEPRECATIONS(statement)
Q_CONSTRUCTOR_FUNCTION(warmUpLocationServices)
void warmUpLocationServices()
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void DBusFreeFunction return DBusConnection return DBusConnection return const char DBusError return DBusConnection DBusMessage dbus_uint32_t return DBusConnection dbus_bool_t DBusConnection DBusAddWatchFunction DBusRemoveWatchFunction DBusWatchToggledFunction void DBusFreeFunction return DBusConnection DBusDispatchStatusFunction void DBusFreeFunction DBusTimeout return DBusTimeout return DBusWatch return DBusWatch unsigned int return DBusError const DBusError return const DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessage return DBusMessageIter int const void return DBusMessageIter DBusMessageIter return DBusMessageIter void DBusMessageIter void int return DBusMessage DBusMessageIter return DBusMessageIter return DBusMessageIter DBusMessageIter const char const char const char const char return DBusMessage return DBusMessage const char return DBusMessage dbus_bool_t return DBusMessage dbus_uint32_t return DBusMessage void
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
static QT_BEGIN_NAMESPACE void init(QTextBoundaryFinder::BoundaryType type, QStringView str, QCharAttributes *attributes)
QNetworkAccessManager manager
QNetworkRequest request(url)