7#include <QtCore/qcoreapplication.h>
8#include <QtCore/qdatetime.h>
9#include <QtCore/private/qfunctions_winrt_p.h>
10#include <QtCore/qloggingcategory.h>
11#include <QtCore/qmutex.h>
12#include <QtCore/qtimezone.h>
15#include <windows.system.h>
16#include <windows.devices.geolocation.h>
17#include <windows.foundation.h>
18#include <windows.foundation.collections.h>
20using namespace Microsoft::WRL;
21using namespace Microsoft::WRL::Wrappers;
23using namespace ABI::Windows::Foundation;
24using namespace ABI::Windows::Foundation::Collections;
31Q_DECLARE_LOGGING_CATEGORY(lcPositioningWinRT)
35static inline HRESULT await(
const ComPtr<IAsyncOperation<GeolocationAccessStatus>> &asyncOp,
36 GeolocationAccessStatus *result)
38 ComPtr<IAsyncInfo> asyncInfo;
39 HRESULT hr = asyncOp.As(&asyncInfo);
44 while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started)
45 QThread::yieldCurrentThread();
47 if (FAILED(hr) || status != AsyncStatus::Completed) {
49 hr = asyncInfo->get_ErrorCode(&ec);
52 hr = asyncInfo->Close();
61 return asyncOp->GetResults(result);
90 : QGeoPositionInfoSource(parent)
91 , d_ptr(
new QGeoPositionInfoSourceWinRTPrivate)
93 qRegisterMetaType<QGeoPositionInfoSource::Error>();
94 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
95 CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED);
96 Q_D(QGeoPositionInfoSourceWinRT);
97 d->positionError = QGeoPositionInfoSource::NoError;
98 d->updatesOngoing =
false;
99 d->positionToken.value = 0;
100 d->statusToken.value = 0;
105 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
111 Q_D(QGeoPositionInfoSourceWinRT);
112 Q_ASSERT(d->initState != InitializationState::Initializing);
113 if (d->initState == InitializationState::Initialized)
116 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
117 d->initState = InitializationState::Initializing;
119 d->initState = InitializationState::Uninitialized;
120 setError(QGeoPositionInfoSource::AccessError);
121 qWarning (
"Location access failed.");
124 HRESULT hr = [
this, d]() {
125 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
127 RETURN_HR_IF_FAILED(
"Could not initialize native location services.");
129 if (d->minimumUpdateInterval == -1) {
131 hr = d->locator->get_ReportInterval(&interval);
132 RETURN_HR_IF_FAILED(
"Could not retrieve report interval.");
133 d->minimumUpdateInterval =
static_cast<
int>(interval);
135 if (d->updateInterval == -1)
136 d->updateInterval = d->minimumUpdateInterval;
142 d->initState = InitializationState::Uninitialized;
143 setError(QGeoPositionInfoSource::UnknownSourceError);
147 d->periodicTimer.setSingleShot(
true);
150 d->singleUpdateTimer.setSingleShot(
true);
153 QGeoPositionInfoSource::PositioningMethods preferredMethods = preferredPositioningMethods();
154 if (preferredMethods == QGeoPositionInfoSource::NoPositioningMethods)
155 preferredMethods = QGeoPositionInfoSource::AllPositioningMethods;
156 setPreferredPositioningMethods(preferredMethods);
159 d->initState = InitializationState::Initialized;
165 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
166 Q_D(
const QGeoPositionInfoSourceWinRT);
167 Q_UNUSED(fromSatellitePositioningMethodsOnly);
168 return d->lastPosition;
173 return requestAccess() ? QGeoPositionInfoSource::AllPositioningMethods
174 : QGeoPositionInfoSource::NoPositioningMethods;
179 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << methods;
180 Q_D(QGeoPositionInfoSourceWinRT);
182 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
183 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
184 if (previousPreferredPositioningMethods == preferredPositioningMethods()
185 || d->initState == InitializationState::Uninitialized) {
189 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
194 PositionAccuracy acc = methods & PositioningMethod::SatellitePositioningMethods ?
195 PositionAccuracy::PositionAccuracy_High :
196 PositionAccuracy::PositionAccuracy_Default;
197 HRESULT hr = [d, acc]() {
198 return d->locator->put_DesiredAccuracy(acc);
200 RETURN_VOID_IF_FAILED(
"Could not set positioning accuracy.");
208 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << msec;
209 Q_D(QGeoPositionInfoSourceWinRT);
210 if (d->initState == InitializationState::Uninitialized) {
211 d->updateInterval = msec;
221 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
226 HRESULT hr = d->locator->put_ReportInterval(
static_cast<UINT32>(msec));
228 setError(QGeoPositionInfoSource::UnknownSourceError);
229 qErrnoWarning(hr,
"Failed to set update interval");
233 d->updateInterval = msec;
234 d->periodicTimer.setInterval(d->updateInterval);
236 QGeoPositionInfoSource::setUpdateInterval(d->updateInterval);
244 Q_D(
const QGeoPositionInfoSourceWinRT);
245 return d->minimumUpdateInterval == -1 ? 1000 : d->minimumUpdateInterval;
250 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
251 Q_D(QGeoPositionInfoSourceWinRT);
253 setError(QGeoPositionInfoSource::NoError);
257 if (d->updatesOngoing)
262 d->updatesOngoing =
true;
263 d->periodicTimer.start();
268 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
269 Q_D(QGeoPositionInfoSourceWinRT);
275 d->updatesOngoing =
false;
276 d->periodicTimer.stop();
281 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
282 Q_D(QGeoPositionInfoSourceWinRT);
285 if (d->positionToken.value != 0)
288 if (preferredPositioningMethods() == QGeoPositionInfoSource::NoPositioningMethods) {
289 setError(QGeoPositionInfoSource::UnknownSourceError);
294 setError(QGeoPositionInfoSource::AccessError);
298 HRESULT hr = [
this, d]() {
304 ComPtr<IAsyncOperation<Geoposition*>> op;
305 hr = d->locator->GetGeopositionAsync(&op);
306 RETURN_HR_IF_FAILED(
"Could not start position operation");
308 hr = d->locator->add_PositionChanged(Callback<GeoLocatorPositionHandler>(
this,
309 &QGeoPositionInfoSourceWinRT::onPositionChanged).Get(),
311 RETURN_HR_IF_FAILED(
"Could not add position handler");
313 hr = d->locator->add_StatusChanged(Callback<GeoLocatorStatusHandler>(
this,
314 &QGeoPositionInfoSourceWinRT::onStatusChanged).Get(),
316 RETURN_HR_IF_FAILED(
"Could not add status handler");
320 setError(QGeoPositionInfoSource::UnknownSourceError);
329 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
330 Q_D(QGeoPositionInfoSourceWinRT);
332 if (!d->positionToken.value)
334 d->locator->remove_PositionChanged(d->positionToken);
335 d->locator->remove_StatusChanged(d->statusToken);
336 d->positionToken.value = 0;
337 d->statusToken.value = 0;
342 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << timeout;
343 Q_D(QGeoPositionInfoSourceWinRT);
348 setError(QGeoPositionInfoSource::NoError);
350 d->positionError = QGeoPositionInfoSource::UpdateTimeoutError;
351 emit QGeoPositionInfoSource::errorOccurred(d->positionError);
359 d->singleUpdateTimer.start(timeout);
364 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
365 Q_D(QGeoPositionInfoSourceWinRT);
366 QMutexLocker locker(&d->mutex);
373 if (d->lastPosition.isValid()) {
374 QGeoPositionInfo sent = d->lastPosition;
375 sent.setTimestamp(sent.timestamp().addMSecs(updateInterval()));
376 d->lastPosition = sent;
377 emit positionUpdated(sent);
379 d->periodicTimer.start();
384 Q_D(QGeoPositionInfoSourceWinRT);
385 QMutexLocker locker(&d->mutex);
387 if (d->singleUpdateTimer.isActive()) {
388 d->positionError = QGeoPositionInfoSource::UpdateTimeoutError;
389 emit QGeoPositionInfoSource::errorOccurred(d->positionError);
390 if (!d->updatesOngoing)
397 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << currentInfo;
398 Q_D(QGeoPositionInfoSourceWinRT);
399 QMutexLocker locker(&d->mutex);
401 d->periodicTimer.stop();
402 d->lastPosition = currentInfo;
404 if (d->updatesOngoing)
405 d->periodicTimer.start();
407 if (d->singleUpdateTimer.isActive()) {
408 d->singleUpdateTimer.stop();
409 if (!d->updatesOngoing)
413 emit positionUpdated(currentInfo);
418 Q_D(
const QGeoPositionInfoSourceWinRT);
419 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << d->positionError;
423 if ((d->positionError == QGeoPositionInfoSource::AccessError
424 || d->positionError == QGeoPositionInfoSource::UnknownSourceError) && requestAccess())
425 return QGeoPositionInfoSource::NoError;
427 return d->positionError;
432 Q_D(QGeoPositionInfoSourceWinRT);
434 if (positionError == d->positionError)
437 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << positionError;
438 d->positionError = positionError;
439 if (positionError != QGeoPositionInfoSource::NoError)
440 emit QGeoPositionInfoSource::errorOccurred(positionError);
445 setError(positionError);
451 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
455 ComPtr<IGeoposition> position;
456 hr = args->get_Position(&position);
457 RETURN_HR_IF_FAILED(
"Could not access position object.");
459 QGeoPositionInfo currentInfo;
461 ComPtr<IGeocoordinate> coord;
462 hr = position->get_Coordinate(&coord);
464 qErrnoWarning(hr,
"Could not access coordinate");
466 ComPtr<IGeocoordinateWithPoint> pointCoordinate;
467 hr = coord.As(&pointCoordinate);
469 qErrnoWarning(hr,
"Could not cast coordinate.");
471 ComPtr<IGeopoint> point;
472 hr = pointCoordinate->get_Point(&point);
474 qErrnoWarning(hr,
"Could not obtain coordinate's point.");
476 BasicGeoposition pos;
477 hr = point->get_Position(&pos);
479 qErrnoWarning(hr,
"Could not obtain point's position.");
481 DOUBLE lat = pos.Latitude;
482 DOUBLE lon = pos.Longitude;
483 DOUBLE alt = pos.Altitude;
485 bool altitudeAvailable =
false;
486 ComPtr<IGeoshape> shape;
487 hr = point.As(&shape);
488 if (SUCCEEDED(hr) && shape) {
489 AltitudeReferenceSystem altitudeSystem;
490 hr = shape->get_AltitudeReferenceSystem(&altitudeSystem);
491 if (SUCCEEDED(hr) && altitudeSystem == AltitudeReferenceSystem_Geoid)
492 altitudeAvailable =
true;
494 if (altitudeAvailable)
495 currentInfo.setCoordinate(QGeoCoordinate(lat, lon, alt));
497 currentInfo.setCoordinate(QGeoCoordinate(lat, lon));
500 hr = coord->get_Accuracy(&accuracy);
502 currentInfo.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy);
504 IReference<
double> *altAccuracy;
505 hr = coord->get_AltitudeAccuracy(&altAccuracy);
506 if (SUCCEEDED(hr) && altAccuracy) {
508 hr = altAccuracy->get_Value(&value);
509 currentInfo.setAttribute(QGeoPositionInfo::VerticalAccuracy, value);
512 IReference<
double> *speed;
513 hr = coord->get_Speed(&speed);
514 if (SUCCEEDED(hr) && speed) {
516 hr = speed->get_Value(&value);
517 currentInfo.setAttribute(QGeoPositionInfo::GroundSpeed, value);
520 IReference<
double> *heading;
521 hr = coord->get_Heading(&heading);
522 if (SUCCEEDED(hr) && heading) {
524 hr = heading->get_Value(&value);
526 value = modf(value, &mod);
527 value +=
static_cast<
int>(mod) % 360;
528 if (value >=0 && value <= 359)
529 currentInfo.setAttribute(QGeoPositionInfo::Direction, value);
533 hr = coord->get_Timestamp(&dateTime);
535 if (dateTime.UniversalTime > 0) {
536 ULARGE_INTEGER uLarge;
537 uLarge.QuadPart = dateTime.UniversalTime;
539 fileTime.dwHighDateTime = uLarge.HighPart;
540 fileTime.dwLowDateTime = uLarge.LowPart;
541 SYSTEMTIME systemTime;
542 if (FileTimeToSystemTime(&fileTime, &systemTime)) {
543 currentInfo.setTimestamp(QDateTime(QDate(systemTime.wYear, systemTime.wMonth,
545 QTime(systemTime.wHour, systemTime.wMinute,
546 systemTime.wSecond, systemTime.wMilliseconds),
551 emit nativePositionUpdate(currentInfo);
558 return status == PositionStatus_NoData || status == PositionStatus_Disabled
559 || status == PositionStatus_NotAvailable;
564 Q_D(QGeoPositionInfoSourceWinRT);
566 const PositionStatus oldStatus = d->positionStatus;
567 HRESULT hr = args->get_Status(&d->positionStatus);
568 RETURN_HR_IF_FAILED(
"Could not obtain position status");
569 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << d->positionStatus;
570 QGeoPositionInfoSource::Error error = QGeoPositionInfoSource::NoError;
571 switch (d->positionStatus) {
572 case PositionStatus::PositionStatus_NotAvailable:
573 error = QGeoPositionInfoSource::UnknownSourceError;
575 case PositionStatus::PositionStatus_Disabled:
576 error = QGeoPositionInfoSource::AccessError;
578 case PositionStatus::PositionStatus_NoData:
579 error = QGeoPositionInfoSource::ClosedError;
582 if (error != QGeoPositionInfoSource::NoError) {
583 QMetaObject::invokeMethod(
this,
"reactOnError", Qt::QueuedConnection,
584 Q_ARG(QGeoPositionInfoSource::Error,
585 QGeoPositionInfoSource::UnknownSourceError));
588 if (isDisabledStatus(oldStatus) != isDisabledStatus(d->positionStatus))
589 emit supportedPositioningMethodsChanged();
596 Q_D(
const QGeoPositionInfoSourceWinRT);
597 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
598 GeolocationAccessStatus accessStatus;
600 ComPtr<IAsyncOperation<GeolocationAccessStatus>> op;
601 HRESULT hr = [&op, d]() {
604 hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
605 IID_PPV_ARGS(&d->statics));
606 RETURN_HR_IF_FAILED(
"Could not access Geolocation Statics.");
609 hr = d->statics->RequestAccessAsync(&op);
613 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ <<
"Requesting access from Xaml thread failed";
617 await(op, &accessStatus);
618 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)