6#include <QtCore/qcoreapplication.h>
7#include <QtCore/qdatetime.h>
8#include <QtCore/private/qfunctions_winrt_p.h>
9#include <QtCore/qloggingcategory.h>
10#include <QtCore/qmutex.h>
11#include <QtCore/qtimezone.h>
14#include <windows.system.h>
15#include <windows.devices.geolocation.h>
16#include <windows.foundation.h>
17#include <windows.foundation.collections.h>
19using namespace Microsoft::WRL;
20using namespace Microsoft::WRL::Wrappers;
22using namespace ABI::Windows::Foundation;
23using namespace ABI::Windows::Foundation::Collections;
30Q_DECLARE_LOGGING_CATEGORY(lcPositioningWinRT)
32Q_DECLARE_METATYPE(QGeoPositionInfoSource::Error)
36static inline HRESULT await(
const ComPtr<IAsyncOperation<GeolocationAccessStatus>> &asyncOp,
37 GeolocationAccessStatus *result)
39 ComPtr<IAsyncInfo> asyncInfo;
40 HRESULT hr = asyncOp.As(&asyncInfo);
45 while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started)
46 QThread::yieldCurrentThread();
48 if (FAILED(hr) || status != AsyncStatus::Completed) {
50 hr = asyncInfo->get_ErrorCode(&ec);
53 hr = asyncInfo->Close();
62 return asyncOp->GetResults(result);
91 : QGeoPositionInfoSource(parent)
92 , d_ptr(
new QGeoPositionInfoSourceWinRTPrivate)
94 qRegisterMetaType<QGeoPositionInfoSource::Error>();
95 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
96 CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED);
97 Q_D(QGeoPositionInfoSourceWinRT);
98 d->positionError = QGeoPositionInfoSource::NoError;
99 d->updatesOngoing =
false;
100 d->positionToken.value = 0;
101 d->statusToken.value = 0;
106 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
112 Q_D(QGeoPositionInfoSourceWinRT);
113 Q_ASSERT(d->initState != InitializationState::Initializing);
114 if (d->initState == InitializationState::Initialized)
117 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
118 d->initState = InitializationState::Initializing;
120 d->initState = InitializationState::Uninitialized;
121 setError(QGeoPositionInfoSource::AccessError);
122 qWarning (
"Location access failed.");
125 HRESULT hr = [
this, d]() {
126 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
128 RETURN_HR_IF_FAILED(
"Could not initialize native location services.");
130 if (d->minimumUpdateInterval == -1) {
132 hr = d->locator->get_ReportInterval(&interval);
133 RETURN_HR_IF_FAILED(
"Could not retrieve report interval.");
134 d->minimumUpdateInterval =
static_cast<
int>(interval);
136 if (d->updateInterval == -1)
137 d->updateInterval = d->minimumUpdateInterval;
138 setUpdateInterval(d->updateInterval);
143 d->initState = InitializationState::Uninitialized;
144 setError(QGeoPositionInfoSource::UnknownSourceError);
148 d->periodicTimer.setSingleShot(
true);
149 connect(&d->periodicTimer, &QTimer::timeout,
this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate);
151 d->singleUpdateTimer.setSingleShot(
true);
152 connect(&d->singleUpdateTimer, &QTimer::timeout,
this, &QGeoPositionInfoSourceWinRT::singleUpdateTimeOut);
154 QGeoPositionInfoSource::PositioningMethods preferredMethods = preferredPositioningMethods();
155 if (preferredMethods == QGeoPositionInfoSource::NoPositioningMethods)
156 preferredMethods = QGeoPositionInfoSource::AllPositioningMethods;
157 setPreferredPositioningMethods(preferredMethods);
159 connect(
this, &QGeoPositionInfoSourceWinRT::nativePositionUpdate,
this, &QGeoPositionInfoSourceWinRT::updateSynchronized);
160 d->initState = InitializationState::Initialized;
166 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
167 Q_D(
const QGeoPositionInfoSourceWinRT);
168 Q_UNUSED(fromSatellitePositioningMethodsOnly);
169 return d->lastPosition;
174 return requestAccess() ? QGeoPositionInfoSource::AllPositioningMethods
175 : QGeoPositionInfoSource::NoPositioningMethods;
180 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << methods;
181 Q_D(QGeoPositionInfoSourceWinRT);
183 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
184 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
185 if (previousPreferredPositioningMethods == preferredPositioningMethods()
186 || d->initState == InitializationState::Uninitialized) {
190 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
195 PositionAccuracy acc = methods & PositioningMethod::SatellitePositioningMethods ?
196 PositionAccuracy::PositionAccuracy_High :
197 PositionAccuracy::PositionAccuracy_Default;
198 HRESULT hr = [d, acc]() {
199 return d->locator->put_DesiredAccuracy(acc);
201 RETURN_VOID_IF_FAILED(
"Could not set positioning accuracy.");
209 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << msec;
210 Q_D(QGeoPositionInfoSourceWinRT);
211 if (d->initState == InitializationState::Uninitialized) {
212 d->updateInterval = msec;
222 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
227 HRESULT hr = d->locator->put_ReportInterval(
static_cast<UINT32>(msec));
229 setError(QGeoPositionInfoSource::UnknownSourceError);
230 qErrnoWarning(hr,
"Failed to set update interval");
234 d->updateInterval = msec;
235 d->periodicTimer.setInterval(d->updateInterval);
237 QGeoPositionInfoSource::setUpdateInterval(d->updateInterval);
245 Q_D(
const QGeoPositionInfoSourceWinRT);
246 return d->minimumUpdateInterval == -1 ? 1000 : d->minimumUpdateInterval;
251 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
252 Q_D(QGeoPositionInfoSourceWinRT);
254 setError(QGeoPositionInfoSource::NoError);
258 if (d->updatesOngoing)
263 d->updatesOngoing =
true;
264 d->periodicTimer.start();
269 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
270 Q_D(QGeoPositionInfoSourceWinRT);
276 d->updatesOngoing =
false;
277 d->periodicTimer.stop();
282 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
283 Q_D(QGeoPositionInfoSourceWinRT);
286 if (d->positionToken.value != 0)
289 if (preferredPositioningMethods() == QGeoPositionInfoSource::NoPositioningMethods) {
290 setError(QGeoPositionInfoSource::UnknownSourceError);
295 setError(QGeoPositionInfoSource::AccessError);
299 HRESULT hr = [
this, d]() {
305 ComPtr<IAsyncOperation<Geoposition*>> op;
306 hr = d->locator->GetGeopositionAsync(&op);
307 RETURN_HR_IF_FAILED(
"Could not start position operation");
309 hr = d->locator->add_PositionChanged(Callback<GeoLocatorPositionHandler>(
this,
310 &QGeoPositionInfoSourceWinRT::onPositionChanged).Get(),
312 RETURN_HR_IF_FAILED(
"Could not add position handler");
314 hr = d->locator->add_StatusChanged(Callback<GeoLocatorStatusHandler>(
this,
315 &QGeoPositionInfoSourceWinRT::onStatusChanged).Get(),
317 RETURN_HR_IF_FAILED(
"Could not add status handler");
321 setError(QGeoPositionInfoSource::UnknownSourceError);
330 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
331 Q_D(QGeoPositionInfoSourceWinRT);
333 if (!d->positionToken.value)
335 d->locator->remove_PositionChanged(d->positionToken);
336 d->locator->remove_StatusChanged(d->statusToken);
337 d->positionToken.value = 0;
338 d->statusToken.value = 0;
343 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << timeout;
344 Q_D(QGeoPositionInfoSourceWinRT);
349 setError(QGeoPositionInfoSource::NoError);
351 d->positionError = QGeoPositionInfoSource::UpdateTimeoutError;
352 emit QGeoPositionInfoSource::errorOccurred(d->positionError);
360 d->singleUpdateTimer.start(timeout);
365 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
366 Q_D(QGeoPositionInfoSourceWinRT);
367 QMutexLocker locker(&d->mutex);
374 if (d->lastPosition.isValid()) {
375 QGeoPositionInfo sent = d->lastPosition;
376 sent.setTimestamp(sent.timestamp().addMSecs(updateInterval()));
377 d->lastPosition = sent;
378 emit positionUpdated(sent);
380 d->periodicTimer.start();
385 Q_D(QGeoPositionInfoSourceWinRT);
386 QMutexLocker locker(&d->mutex);
388 if (d->singleUpdateTimer.isActive()) {
389 d->positionError = QGeoPositionInfoSource::UpdateTimeoutError;
390 emit QGeoPositionInfoSource::errorOccurred(d->positionError);
391 if (!d->updatesOngoing)
398 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << currentInfo;
399 Q_D(QGeoPositionInfoSourceWinRT);
400 QMutexLocker locker(&d->mutex);
402 d->periodicTimer.stop();
403 d->lastPosition = currentInfo;
405 if (d->updatesOngoing)
406 d->periodicTimer.start();
408 if (d->singleUpdateTimer.isActive()) {
409 d->singleUpdateTimer.stop();
410 if (!d->updatesOngoing)
414 emit positionUpdated(currentInfo);
419 Q_D(
const QGeoPositionInfoSourceWinRT);
420 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << d->positionError;
424 if ((d->positionError == QGeoPositionInfoSource::AccessError
425 || d->positionError == QGeoPositionInfoSource::UnknownSourceError) && requestAccess())
426 return QGeoPositionInfoSource::NoError;
428 return d->positionError;
433 Q_D(QGeoPositionInfoSourceWinRT);
435 if (positionError == d->positionError)
438 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << positionError;
439 d->positionError = positionError;
440 if (positionError != QGeoPositionInfoSource::NoError)
441 emit QGeoPositionInfoSource::errorOccurred(positionError);
446 setError(positionError);
452 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
456 ComPtr<IGeoposition> position;
457 hr = args->get_Position(&position);
458 RETURN_HR_IF_FAILED(
"Could not access position object.");
460 QGeoPositionInfo currentInfo;
462 ComPtr<IGeocoordinate> coord;
463 hr = position->get_Coordinate(&coord);
465 qErrnoWarning(hr,
"Could not access coordinate");
467 ComPtr<IGeocoordinateWithPoint> pointCoordinate;
468 hr = coord.As(&pointCoordinate);
470 qErrnoWarning(hr,
"Could not cast coordinate.");
472 ComPtr<IGeopoint> point;
473 hr = pointCoordinate->get_Point(&point);
475 qErrnoWarning(hr,
"Could not obtain coordinate's point.");
477 BasicGeoposition pos;
478 hr = point->get_Position(&pos);
480 qErrnoWarning(hr,
"Could not obtain point's position.");
482 DOUBLE lat = pos.Latitude;
483 DOUBLE lon = pos.Longitude;
484 DOUBLE alt = pos.Altitude;
486 bool altitudeAvailable =
false;
487 ComPtr<IGeoshape> shape;
488 hr = point.As(&shape);
489 if (SUCCEEDED(hr) && shape) {
490 AltitudeReferenceSystem altitudeSystem;
491 hr = shape->get_AltitudeReferenceSystem(&altitudeSystem);
492 if (SUCCEEDED(hr) && altitudeSystem == AltitudeReferenceSystem_Geoid)
493 altitudeAvailable =
true;
495 if (altitudeAvailable)
496 currentInfo.setCoordinate(QGeoCoordinate(lat, lon, alt));
498 currentInfo.setCoordinate(QGeoCoordinate(lat, lon));
501 hr = coord->get_Accuracy(&accuracy);
503 currentInfo.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy);
505 IReference<
double> *altAccuracy;
506 hr = coord->get_AltitudeAccuracy(&altAccuracy);
507 if (SUCCEEDED(hr) && altAccuracy) {
509 hr = altAccuracy->get_Value(&value);
510 currentInfo.setAttribute(QGeoPositionInfo::VerticalAccuracy, value);
513 IReference<
double> *speed;
514 hr = coord->get_Speed(&speed);
515 if (SUCCEEDED(hr) && speed) {
517 hr = speed->get_Value(&value);
518 currentInfo.setAttribute(QGeoPositionInfo::GroundSpeed, value);
521 IReference<
double> *heading;
522 hr = coord->get_Heading(&heading);
523 if (SUCCEEDED(hr) && heading) {
525 hr = heading->get_Value(&value);
527 value = modf(value, &mod);
528 value +=
static_cast<
int>(mod) % 360;
529 if (value >=0 && value <= 359)
530 currentInfo.setAttribute(QGeoPositionInfo::Direction, value);
534 hr = coord->get_Timestamp(&dateTime);
536 if (dateTime.UniversalTime > 0) {
537 ULARGE_INTEGER uLarge;
538 uLarge.QuadPart = dateTime.UniversalTime;
540 fileTime.dwHighDateTime = uLarge.HighPart;
541 fileTime.dwLowDateTime = uLarge.LowPart;
542 SYSTEMTIME systemTime;
543 if (FileTimeToSystemTime(&fileTime, &systemTime)) {
544 currentInfo.setTimestamp(QDateTime(QDate(systemTime.wYear, systemTime.wMonth,
546 QTime(systemTime.wHour, systemTime.wMinute,
547 systemTime.wSecond, systemTime.wMilliseconds),
552 emit nativePositionUpdate(currentInfo);
559 return status == PositionStatus_NoData || status == PositionStatus_Disabled
560 || status == PositionStatus_NotAvailable;
565 Q_D(QGeoPositionInfoSourceWinRT);
567 const PositionStatus oldStatus = d->positionStatus;
568 HRESULT hr = args->get_Status(&d->positionStatus);
569 RETURN_HR_IF_FAILED(
"Could not obtain position status");
570 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << d->positionStatus;
571 QGeoPositionInfoSource::Error error = QGeoPositionInfoSource::NoError;
572 switch (d->positionStatus) {
573 case PositionStatus::PositionStatus_NotAvailable:
574 error = QGeoPositionInfoSource::UnknownSourceError;
576 case PositionStatus::PositionStatus_Disabled:
577 error = QGeoPositionInfoSource::AccessError;
579 case PositionStatus::PositionStatus_NoData:
580 error = QGeoPositionInfoSource::ClosedError;
583 if (error != QGeoPositionInfoSource::NoError) {
584 QMetaObject::invokeMethod(
this,
"reactOnError", Qt::QueuedConnection,
585 Q_ARG(QGeoPositionInfoSource::Error,
586 QGeoPositionInfoSource::UnknownSourceError));
589 if (isDisabledStatus(oldStatus) != isDisabledStatus(d->positionStatus))
590 emit supportedPositioningMethodsChanged();
597 Q_D(
const QGeoPositionInfoSourceWinRT);
598 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
599 GeolocationAccessStatus accessStatus;
601 ComPtr<IAsyncOperation<GeolocationAccessStatus>> op;
602 HRESULT hr = [&op, d]() {
605 hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
606 IID_PPV_ARGS(&d->statics));
607 RETURN_HR_IF_FAILED(
"Could not access Geolocation Statics.");
610 hr = d->statics->RequestAccessAsync(&op);
614 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ <<
"Requesting access from Xaml thread failed";
618 await(op, &accessStatus);
619 return accessStatus == GeolocationAccessStatus_Allowed;
QGeoPositionInfo lastPosition
InitializationState initState
EventRegistrationToken positionToken
PositionStatus positionStatus
ComPtr< IGeolocator > locator
int minimumUpdateInterval
ComPtr< IGeolocatorStatics > statics
EventRegistrationToken statusToken
void setUpdateInterval(int msec) override
HRESULT onStatusChanged(ABI::Windows::Devices::Geolocation::IGeolocator *locator, ABI::Windows::Devices::Geolocation::IStatusChangedEventArgs *args)
QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly=false) const override
Returns an update containing the last known position, or a null update if none is available.
bool requestAccess() const
Error error() const override
Returns the type of error that last occurred.
~QGeoPositionInfoSourceWinRT() override
int minimumUpdateInterval() const override
HRESULT onPositionChanged(ABI::Windows::Devices::Geolocation::IGeolocator *locator, ABI::Windows::Devices::Geolocation::IPositionChangedEventArgs *args)
void requestUpdate(int timeout=0) override
void setPreferredPositioningMethods(PositioningMethods methods) override
void stopUpdates() override
PositioningMethods supportedPositioningMethods() const override
Returns the positioning methods available to this source.
IAsyncOperationCompletedHandler< Geoposition * > PositionHandler
ITypedEventHandler< Geolocator *, StatusChangedEventArgs * > GeoLocatorStatusHandler
ITypedEventHandler< Geolocator *, PositionChangedEventArgs * > GeoLocatorPositionHandler
IAsyncOperationCompletedHandler< GeolocationAccessStatus > AccessHandler
static bool isDisabledStatus(PositionStatus status)