1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtPositioning module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL3$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or later as published by the Free
28 ** Software Foundation and appearing in the file LICENSE.GPL included in
29 ** the packaging of this file. Please review the following information to
30 ** ensure the GNU General Public License version 2.0 requirements will be
31 ** met: http://www.gnu.org/licenses/gpl-2.0.html.
32 **
33 ** $QT_END_LICENSE$
34 **
35 ****************************************************************************/
36
37 #include "qgeopositioninfosource_winrt_p.h"
38
39 #include <QtCore/qcoreapplication.h>
40 #include <QtCore/qfunctions_winrt.h>
41 #include <QtCore/qloggingcategory.h>
42 #include <QtCore/qmutex.h>
43 #ifdef Q_OS_WINRT
44 #include <QtCore/private/qeventdispatcher_winrt_p.h>
45 #endif
46
47 #include <functional>
48 #include <windows.system.h>
49 #include <windows.devices.geolocation.h>
50 #include <windows.foundation.h>
51 #include <windows.foundation.collections.h>
52
53 using namespace Microsoft::WRL;
54 using namespace Microsoft::WRL::Wrappers;
55 using namespace ABI::Windows::Devices::Geolocation;
56 using namespace ABI::Windows::Foundation;
57 using namespace ABI::Windows::Foundation::Collections;
58
59 typedef ITypedEventHandler<Geolocator *, PositionChangedEventArgs *> GeoLocatorPositionHandler;
60 typedef ITypedEventHandler<Geolocator *, StatusChangedEventArgs *> GeoLocatorStatusHandler;
61 typedef IAsyncOperationCompletedHandler<Geoposition*> PositionHandler;
62 typedef IAsyncOperationCompletedHandler<GeolocationAccessStatus> AccessHandler;
63
64 Q_DECLARE_LOGGING_CATEGORY(lcPositioningWinRT)
65
66 Q_DECLARE_METATYPE(QGeoPositionInfoSource::Error)
67
68 QT_BEGIN_NAMESPACE
69
70 #ifndef Q_OS_WINRT
71 namespace QEventDispatcherWinRT {
runOnXamlThread(const std::function<HRESULT ()> & delegate,bool waitForRun=true)72 HRESULT runOnXamlThread(const std::function<HRESULT ()> &delegate, bool waitForRun = true)
73 {
74 Q_UNUSED(waitForRun);
75 return delegate();
76 }
77 }
78
await(const ComPtr<IAsyncOperation<GeolocationAccessStatus>> & asyncOp,GeolocationAccessStatus * result)79 static inline HRESULT await(const ComPtr<IAsyncOperation<GeolocationAccessStatus>> &asyncOp,
80 GeolocationAccessStatus *result)
81 {
82 ComPtr<IAsyncInfo> asyncInfo;
83 HRESULT hr = asyncOp.As(&asyncInfo);
84 if (FAILED(hr))
85 return hr;
86
87 AsyncStatus status;
88 while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started)
89 QThread::yieldCurrentThread();
90
91 if (FAILED(hr) || status != AsyncStatus::Completed) {
92 HRESULT ec;
93 hr = asyncInfo->get_ErrorCode(&ec);
94 if (FAILED(hr))
95 return hr;
96 hr = asyncInfo->Close();
97 if (FAILED(hr))
98 return hr;
99 return ec;
100 }
101
102 if (FAILED(hr))
103 return hr;
104
105 return asyncOp->GetResults(result);
106 }
107 #endif // !Q_OS_WINRT
108
109 enum class InitializationState {
110 Uninitialized,
111 Initializing,
112 Initialized
113 };
114
115 class QGeoPositionInfoSourceWinRTPrivate {
116 public:
117 ComPtr<IGeolocator> locator;
118 mutable ComPtr<IGeolocatorStatics> statics;
119 QTimer periodicTimer;
120 QTimer singleUpdateTimer;
121 QGeoPositionInfo lastPosition;
122 QGeoPositionInfoSource::Error positionError = QGeoPositionInfoSource::NoError;
123 EventRegistrationToken statusToken;
124 EventRegistrationToken positionToken;
125 QMutex mutex;
126 bool updatesOngoing = false;
127 int minimumUpdateInterval = -1;
128 int updateInterval = -1;
129 InitializationState initState = InitializationState::Uninitialized;
130
131 PositionStatus positionStatus = PositionStatus_NotInitialized;
132 };
133
QGeoPositionInfoSourceWinRT(QObject * parent)134 QGeoPositionInfoSourceWinRT::QGeoPositionInfoSourceWinRT(QObject *parent)
135 : QGeoPositionInfoSource(parent)
136 , d_ptr(new QGeoPositionInfoSourceWinRTPrivate)
137 {
138 qRegisterMetaType<QGeoPositionInfoSource::Error>();
139 qCDebug(lcPositioningWinRT) << __FUNCTION__;
140 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
141 Q_D(QGeoPositionInfoSourceWinRT);
142 d->positionError = QGeoPositionInfoSource::NoError;
143 d->updatesOngoing = false;
144 d->positionToken.value = 0;
145 d->statusToken.value = 0;
146 }
147
~QGeoPositionInfoSourceWinRT()148 QGeoPositionInfoSourceWinRT::~QGeoPositionInfoSourceWinRT()
149 {
150 qCDebug(lcPositioningWinRT) << __FUNCTION__;
151 CoUninitialize();
152 }
153
init()154 int QGeoPositionInfoSourceWinRT::init()
155 {
156 Q_D(QGeoPositionInfoSourceWinRT);
157 Q_ASSERT(d->initState != InitializationState::Initializing);
158 if (d->initState == InitializationState::Initialized)
159 return 0;
160
161 qCDebug(lcPositioningWinRT) << __FUNCTION__;
162 d->initState = InitializationState::Initializing;
163 if (!requestAccess()) {
164 d->initState = InitializationState::Uninitialized;
165 setError(QGeoPositionInfoSource::AccessError);
166 qWarning ("Location access failed.");
167 return -1;
168 }
169 HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() {
170 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
171 &d->locator);
172 RETURN_HR_IF_FAILED("Could not initialize native location services.");
173
174 if (d->minimumUpdateInterval == -1) {
175 UINT32 interval;
176 hr = d->locator->get_ReportInterval(&interval);
177 RETURN_HR_IF_FAILED("Could not retrieve report interval.");
178 d->minimumUpdateInterval = static_cast<int>(interval);
179 }
180 if (d->updateInterval == -1)
181 d->updateInterval = d->minimumUpdateInterval;
182 setUpdateInterval(d->updateInterval);
183
184 return hr;
185 });
186 if (FAILED(hr)) {
187 d->initState = InitializationState::Uninitialized;
188 setError(QGeoPositionInfoSource::UnknownSourceError);
189 return -1;
190 }
191
192 d->periodicTimer.setSingleShot(true);
193 connect(&d->periodicTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate);
194
195 d->singleUpdateTimer.setSingleShot(true);
196 connect(&d->singleUpdateTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::singleUpdateTimeOut);
197
198 QGeoPositionInfoSource::PositioningMethods preferredMethods = preferredPositioningMethods();
199 if (preferredMethods == QGeoPositionInfoSource::NoPositioningMethods)
200 preferredMethods = QGeoPositionInfoSource::AllPositioningMethods;
201 setPreferredPositioningMethods(preferredMethods);
202
203 connect(this, &QGeoPositionInfoSourceWinRT::nativePositionUpdate, this, &QGeoPositionInfoSourceWinRT::updateSynchronized);
204 d->initState = InitializationState::Initialized;
205 return 0;
206 }
207
lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const208 QGeoPositionInfo QGeoPositionInfoSourceWinRT::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
209 {
210 qCDebug(lcPositioningWinRT) << __FUNCTION__;
211 Q_D(const QGeoPositionInfoSourceWinRT);
212 Q_UNUSED(fromSatellitePositioningMethodsOnly);
213 return d->lastPosition;
214 }
215
supportedPositioningMethods() const216 QGeoPositionInfoSource::PositioningMethods QGeoPositionInfoSourceWinRT::supportedPositioningMethods() const
217 {
218 return requestAccess() ? QGeoPositionInfoSource::AllPositioningMethods
219 : QGeoPositionInfoSource::NoPositioningMethods;
220 }
221
setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)222 void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)
223 {
224 qCDebug(lcPositioningWinRT) << __FUNCTION__ << methods;
225 Q_D(QGeoPositionInfoSourceWinRT);
226
227 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
228 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
229 if (previousPreferredPositioningMethods == preferredPositioningMethods()
230 || d->initState == InitializationState::Uninitialized) {
231 return;
232 }
233
234 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
235
236 if (needsRestart)
237 stopHandler();
238
239 PositionAccuracy acc = methods & PositioningMethod::SatellitePositioningMethods ?
240 PositionAccuracy::PositionAccuracy_High :
241 PositionAccuracy::PositionAccuracy_Default;
242 HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([d, acc]() {
243 return d->locator->put_DesiredAccuracy(acc);
244 });
245 RETURN_VOID_IF_FAILED("Could not set positioning accuracy.");
246
247 if (needsRestart)
248 startHandler();
249 }
250
setUpdateInterval(int msec)251 void QGeoPositionInfoSourceWinRT::setUpdateInterval(int msec)
252 {
253 qCDebug(lcPositioningWinRT) << __FUNCTION__ << msec;
254 Q_D(QGeoPositionInfoSourceWinRT);
255 if (d->initState == InitializationState::Uninitialized) {
256 d->updateInterval = msec;
257 return;
258 }
259
260 // minimumUpdateInterval is initialized to the lowest possible update interval in init().
261 // Passing 0 will cause an error on Windows 10.
262 // See https://docs.microsoft.com/en-us/uwp/api/windows.devices.geolocation.geolocator.reportinterval
263 if (msec != 0 && msec < minimumUpdateInterval())
264 msec = minimumUpdateInterval();
265
266 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
267
268 if (needsRestart)
269 stopHandler();
270
271 HRESULT hr = d->locator->put_ReportInterval(static_cast<UINT32>(msec));
272 if (FAILED(hr)) {
273 setError(QGeoPositionInfoSource::UnknownSourceError);
274 qErrnoWarning(hr, "Failed to set update interval");
275 return;
276 }
277
278 d->updateInterval = msec;
279 d->periodicTimer.setInterval(d->updateInterval);
280
281 QGeoPositionInfoSource::setUpdateInterval(d->updateInterval);
282
283 if (needsRestart)
284 startHandler();
285 }
286
minimumUpdateInterval() const287 int QGeoPositionInfoSourceWinRT::minimumUpdateInterval() const
288 {
289 Q_D(const QGeoPositionInfoSourceWinRT);
290 return d->minimumUpdateInterval == -1 ? 1000 : d->minimumUpdateInterval;
291 }
292
startUpdates()293 void QGeoPositionInfoSourceWinRT::startUpdates()
294 {
295 qCDebug(lcPositioningWinRT) << __FUNCTION__;
296 Q_D(QGeoPositionInfoSourceWinRT);
297
298 setError(QGeoPositionInfoSource::NoError);
299 if (init() < 0)
300 return;
301
302 if (d->updatesOngoing)
303 return;
304
305 if (!startHandler())
306 return;
307 d->updatesOngoing = true;
308 d->periodicTimer.start();
309 }
310
stopUpdates()311 void QGeoPositionInfoSourceWinRT::stopUpdates()
312 {
313 qCDebug(lcPositioningWinRT) << __FUNCTION__;
314 Q_D(QGeoPositionInfoSourceWinRT);
315
316 if (init() < 0)
317 return;
318
319 stopHandler();
320 d->updatesOngoing = false;
321 d->periodicTimer.stop();
322 }
323
startHandler()324 bool QGeoPositionInfoSourceWinRT::startHandler()
325 {
326 qCDebug(lcPositioningWinRT) << __FUNCTION__;
327 Q_D(QGeoPositionInfoSourceWinRT);
328
329 // Check if already attached
330 if (d->positionToken.value != 0)
331 return true;
332
333 if (preferredPositioningMethods() == QGeoPositionInfoSource::NoPositioningMethods) {
334 setError(QGeoPositionInfoSource::UnknownSourceError);
335 return false;
336 }
337
338 if (!requestAccess()) {
339 setError(QGeoPositionInfoSource::AccessError);
340 return false;
341 }
342
343 HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, d]() {
344 HRESULT hr;
345
346 // We need to call this at least once on Windows 10 Mobile.
347 // Unfortunately this operation does not have a completion handler
348 // registered. That could have helped in the single update case
349 ComPtr<IAsyncOperation<Geoposition*>> op;
350 hr = d->locator->GetGeopositionAsync(&op);
351 RETURN_HR_IF_FAILED("Could not start position operation");
352
353 hr = d->locator->add_PositionChanged(Callback<GeoLocatorPositionHandler>(this,
354 &QGeoPositionInfoSourceWinRT::onPositionChanged).Get(),
355 &d->positionToken);
356 RETURN_HR_IF_FAILED("Could not add position handler");
357
358 hr = d->locator->add_StatusChanged(Callback<GeoLocatorStatusHandler>(this,
359 &QGeoPositionInfoSourceWinRT::onStatusChanged).Get(),
360 &d->statusToken);
361 RETURN_HR_IF_FAILED("Could not add status handler");
362 return hr;
363 });
364 if (FAILED(hr)) {
365 setError(QGeoPositionInfoSource::UnknownSourceError);
366 return false;
367 }
368
369 return true;
370 }
371
stopHandler()372 void QGeoPositionInfoSourceWinRT::stopHandler()
373 {
374 qCDebug(lcPositioningWinRT) << __FUNCTION__;
375 Q_D(QGeoPositionInfoSourceWinRT);
376
377 if (!d->positionToken.value)
378 return;
379 QEventDispatcherWinRT::runOnXamlThread([d]() {
380 d->locator->remove_PositionChanged(d->positionToken);
381 d->locator->remove_StatusChanged(d->statusToken);
382 return S_OK;
383 });
384 d->positionToken.value = 0;
385 d->statusToken.value = 0;
386 }
387
requestUpdate(int timeout)388 void QGeoPositionInfoSourceWinRT::requestUpdate(int timeout)
389 {
390 qCDebug(lcPositioningWinRT) << __FUNCTION__ << timeout;
391 Q_D(QGeoPositionInfoSourceWinRT);
392
393 if (init() < 0)
394 return;
395
396 setError(QGeoPositionInfoSource::NoError);
397 if (timeout != 0 && timeout < minimumUpdateInterval()) {
398 emit updateTimeout();
399 return;
400 }
401
402 if (timeout == 0)
403 timeout = 2*60*1000; // Maximum time for cold start (see Android)
404
405 startHandler();
406 d->singleUpdateTimer.start(timeout);
407 }
408
virtualPositionUpdate()409 void QGeoPositionInfoSourceWinRT::virtualPositionUpdate()
410 {
411 qCDebug(lcPositioningWinRT) << __FUNCTION__;
412 Q_D(QGeoPositionInfoSourceWinRT);
413 QMutexLocker locker(&d->mutex);
414
415 // The operating system did not provide information in time
416 // Hence we send a virtual position update to keep same behavior
417 // between backends.
418 // This only applies to the periodic timer, not for single requests
419 // We can only do this if we received a valid position before
420 if (d->lastPosition.isValid()) {
421 QGeoPositionInfo sent = d->lastPosition;
422 sent.setTimestamp(sent.timestamp().addMSecs(updateInterval()));
423 d->lastPosition = sent;
424 emit positionUpdated(sent);
425 }
426 d->periodicTimer.start();
427 }
428
singleUpdateTimeOut()429 void QGeoPositionInfoSourceWinRT::singleUpdateTimeOut()
430 {
431 Q_D(QGeoPositionInfoSourceWinRT);
432 QMutexLocker locker(&d->mutex);
433
434 if (d->singleUpdateTimer.isActive()) {
435 emit updateTimeout();
436 if (!d->updatesOngoing)
437 stopHandler();
438 }
439 }
440
updateSynchronized(QGeoPositionInfo currentInfo)441 void QGeoPositionInfoSourceWinRT::updateSynchronized(QGeoPositionInfo currentInfo)
442 {
443 qCDebug(lcPositioningWinRT) << __FUNCTION__ << currentInfo;
444 Q_D(QGeoPositionInfoSourceWinRT);
445 QMutexLocker locker(&d->mutex);
446
447 d->periodicTimer.stop();
448 d->lastPosition = currentInfo;
449
450 if (d->updatesOngoing)
451 d->periodicTimer.start();
452
453 if (d->singleUpdateTimer.isActive()) {
454 d->singleUpdateTimer.stop();
455 if (!d->updatesOngoing)
456 stopHandler();
457 }
458
459 emit positionUpdated(currentInfo);
460 }
461
error() const462 QGeoPositionInfoSource::Error QGeoPositionInfoSourceWinRT::error() const
463 {
464 Q_D(const QGeoPositionInfoSourceWinRT);
465 qCDebug(lcPositioningWinRT) << __FUNCTION__ << d->positionError;
466
467 // If the last encountered error was "Access denied", it is possible that the location service
468 // has been enabled by now so that we are clear again.
469 if ((d->positionError == QGeoPositionInfoSource::AccessError
470 || d->positionError == QGeoPositionInfoSource::UnknownSourceError) && requestAccess())
471 return QGeoPositionInfoSource::NoError;
472
473 return d->positionError;
474 }
475
setError(QGeoPositionInfoSource::Error positionError)476 void QGeoPositionInfoSourceWinRT::setError(QGeoPositionInfoSource::Error positionError)
477 {
478 Q_D(QGeoPositionInfoSourceWinRT);
479
480 if (positionError == d->positionError)
481 return;
482
483 qCDebug(lcPositioningWinRT) << __FUNCTION__ << positionError;
484 d->positionError = positionError;
485 if (positionError != QGeoPositionInfoSource::NoError)
486 emit QGeoPositionInfoSource::error(positionError);
487 }
488
reactOnError(QGeoPositionInfoSource::Error positionError)489 void QGeoPositionInfoSourceWinRT::reactOnError(QGeoPositionInfoSource::Error positionError)
490 {
491 setError(positionError);
492 stopUpdates();
493 }
494
onPositionChanged(IGeolocator * locator,IPositionChangedEventArgs * args)495 HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPositionChangedEventArgs *args)
496 {
497 qCDebug(lcPositioningWinRT) << __FUNCTION__;
498 Q_UNUSED(locator);
499
500 HRESULT hr;
501 ComPtr<IGeoposition> position;
502 hr = args->get_Position(&position);
503 RETURN_HR_IF_FAILED("Could not access position object.");
504
505 QGeoPositionInfo currentInfo;
506
507 ComPtr<IGeocoordinate> coord;
508 hr = position->get_Coordinate(&coord);
509 if (FAILED(hr))
510 qErrnoWarning(hr, "Could not access coordinate");
511
512 ComPtr<IGeocoordinateWithPoint> pointCoordinate;
513 hr = coord.As(&pointCoordinate);
514 if (FAILED(hr))
515 qErrnoWarning(hr, "Could not cast coordinate.");
516
517 ComPtr<IGeopoint> point;
518 hr = pointCoordinate->get_Point(&point);
519 if (FAILED(hr))
520 qErrnoWarning(hr, "Could not obtain coordinate's point.");
521
522 BasicGeoposition pos;
523 hr = point->get_Position(&pos);
524 if (FAILED(hr))
525 qErrnoWarning(hr, "Could not obtain point's position.");
526
527 DOUBLE lat = pos.Latitude;
528 DOUBLE lon = pos.Longitude;
529 DOUBLE alt = pos.Altitude;
530
531 bool altitudeAvailable = false;
532 ComPtr<IGeoshape> shape;
533 hr = point.As(&shape);
534 if (SUCCEEDED(hr) && shape) {
535 AltitudeReferenceSystem altitudeSystem;
536 hr = shape->get_AltitudeReferenceSystem(&altitudeSystem);
537 if (SUCCEEDED(hr) && altitudeSystem == AltitudeReferenceSystem_Geoid)
538 altitudeAvailable = true;
539 }
540 if (altitudeAvailable)
541 currentInfo.setCoordinate(QGeoCoordinate(lat, lon, alt));
542 else
543 currentInfo.setCoordinate(QGeoCoordinate(lat, lon));
544
545 DOUBLE accuracy;
546 hr = coord->get_Accuracy(&accuracy);
547 if (SUCCEEDED(hr))
548 currentInfo.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy);
549
550 IReference<double> *altAccuracy;
551 hr = coord->get_AltitudeAccuracy(&altAccuracy);
552 if (SUCCEEDED(hr) && altAccuracy) {
553 double value;
554 hr = altAccuracy->get_Value(&value);
555 currentInfo.setAttribute(QGeoPositionInfo::VerticalAccuracy, value);
556 }
557
558 IReference<double> *speed;
559 hr = coord->get_Speed(&speed);
560 if (SUCCEEDED(hr) && speed) {
561 double value;
562 hr = speed->get_Value(&value);
563 currentInfo.setAttribute(QGeoPositionInfo::GroundSpeed, value);
564 }
565
566 IReference<double> *heading;
567 hr = coord->get_Heading(&heading);
568 if (SUCCEEDED(hr) && heading) {
569 double value;
570 hr = heading->get_Value(&value);
571 double mod = 0;
572 value = modf(value, &mod);
573 value += static_cast<int>(mod) % 360;
574 if (value >=0 && value <= 359) // get_Value might return nan/-nan
575 currentInfo.setAttribute(QGeoPositionInfo::Direction, value);
576 }
577
578 DateTime dateTime;
579 hr = coord->get_Timestamp(&dateTime);
580
581 if (dateTime.UniversalTime > 0) {
582 ULARGE_INTEGER uLarge;
583 uLarge.QuadPart = dateTime.UniversalTime;
584 FILETIME fileTime;
585 fileTime.dwHighDateTime = uLarge.HighPart;
586 fileTime.dwLowDateTime = uLarge.LowPart;
587 SYSTEMTIME systemTime;
588 if (FileTimeToSystemTime(&fileTime, &systemTime)) {
589 currentInfo.setTimestamp(QDateTime(QDate(systemTime.wYear, systemTime.wMonth,
590 systemTime.wDay),
591 QTime(systemTime.wHour, systemTime.wMinute,
592 systemTime.wSecond, systemTime.wMilliseconds),
593 Qt::UTC));
594 }
595 }
596
597 emit nativePositionUpdate(currentInfo);
598
599 return S_OK;
600 }
601
isDisabledStatus(PositionStatus status)602 static inline bool isDisabledStatus(PositionStatus status)
603 {
604 return status == PositionStatus_NoData || status == PositionStatus_Disabled
605 || status == PositionStatus_NotAvailable;
606 }
607
onStatusChanged(IGeolocator *,IStatusChangedEventArgs * args)608 HRESULT QGeoPositionInfoSourceWinRT::onStatusChanged(IGeolocator *, IStatusChangedEventArgs *args)
609 {
610 Q_D(QGeoPositionInfoSourceWinRT);
611
612 const PositionStatus oldStatus = d->positionStatus;
613 HRESULT hr = args->get_Status(&d->positionStatus);
614 RETURN_HR_IF_FAILED("Could not obtain position status");
615 qCDebug(lcPositioningWinRT) << __FUNCTION__ << d->positionStatus;
616 QGeoPositionInfoSource::Error error = QGeoPositionInfoSource::NoError;
617 switch (d->positionStatus) {
618 case PositionStatus::PositionStatus_NotAvailable:
619 error = QGeoPositionInfoSource::UnknownSourceError;
620 break;
621 case PositionStatus::PositionStatus_Disabled:
622 error = QGeoPositionInfoSource::AccessError;
623 break;
624 case PositionStatus::PositionStatus_NoData:
625 error = QGeoPositionInfoSource::ClosedError;
626 break;
627 }
628 if (error != QGeoPositionInfoSource::NoError) {
629 QMetaObject::invokeMethod(this, "reactOnError", Qt::QueuedConnection,
630 Q_ARG(QGeoPositionInfoSource::Error,
631 QGeoPositionInfoSource::UnknownSourceError));
632 }
633
634 if (isDisabledStatus(oldStatus) != isDisabledStatus(d->positionStatus))
635 emit supportedPositioningMethodsChanged();
636
637 return S_OK;
638 }
639
requestAccess() const640 bool QGeoPositionInfoSourceWinRT::requestAccess() const
641 {
642 Q_D(const QGeoPositionInfoSourceWinRT);
643 qCDebug(lcPositioningWinRT) << __FUNCTION__;
644 GeolocationAccessStatus accessStatus;
645
646 ComPtr<IAsyncOperation<GeolocationAccessStatus>> op;
647 HRESULT hr;
648 hr = QEventDispatcherWinRT::runOnXamlThread([&op, d]() {
649 HRESULT hr;
650 if (!d->statics) {
651 hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
652 IID_PPV_ARGS(&d->statics));
653 RETURN_HR_IF_FAILED("Could not access Geolocation Statics.");
654 }
655
656 hr = d->statics->RequestAccessAsync(&op);
657 return hr;
658 });
659 if (FAILED(hr)) {
660 qCDebug(lcPositioningWinRT) << __FUNCTION__ << "Requesting access from Xaml thread failed";
661 return false;
662 }
663
664 // We cannot wait inside the XamlThread as that would deadlock
665 #ifdef Q_OS_WINRT
666 QWinRTFunctions::await(op, &accessStatus);
667 #else
668 await(op, &accessStatus);
669 #endif
670 return accessStatus == GeolocationAccessStatus_Allowed;
671 }
672
673 QT_END_NAMESPACE
674