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)
34static inline HRESULT await(
const ComPtr<IAsyncOperation<GeolocationAccessStatus>> &asyncOp,
35 GeolocationAccessStatus *result)
37 ComPtr<IAsyncInfo> asyncInfo;
38 HRESULT hr = asyncOp.As(&asyncInfo);
43 while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started)
44 QThread::yieldCurrentThread();
46 if (FAILED(hr) || status != AsyncStatus::Completed) {
48 hr = asyncInfo->get_ErrorCode(&ec);
51 hr = asyncInfo->Close();
60 return asyncOp->GetResults(result);
89 : QGeoPositionInfoSource(parent)
90 , d_ptr(
new QGeoPositionInfoSourceWinRTPrivate)
92 qRegisterMetaType<QGeoPositionInfoSource::Error>();
93 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
94 CoInitializeEx(
nullptr, COINIT_APARTMENTTHREADED);
95 Q_D(QGeoPositionInfoSourceWinRT);
96 d->positionError = QGeoPositionInfoSource::NoError;
97 d->updatesOngoing =
false;
98 d->positionToken.value = 0;
99 d->statusToken.value = 0;
104 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
110 Q_D(QGeoPositionInfoSourceWinRT);
111 Q_ASSERT(d->initState != InitializationState::Initializing);
112 if (d->initState == InitializationState::Initialized)
115 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
116 d->initState = InitializationState::Initializing;
118 d->initState = InitializationState::Uninitialized;
119 setError(QGeoPositionInfoSource::AccessError);
120 qWarning (
"Location access failed.");
123 HRESULT hr = [
this, d]() {
124 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
126 RETURN_HR_IF_FAILED(
"Could not initialize native location services.");
128 if (d->minimumUpdateInterval == -1) {
130 hr = d->locator->get_ReportInterval(&interval);
131 RETURN_HR_IF_FAILED(
"Could not retrieve report interval.");
132 d->minimumUpdateInterval =
static_cast<
int>(interval);
134 if (d->updateInterval == -1)
135 d->updateInterval = d->minimumUpdateInterval;
136 setUpdateInterval(d->updateInterval);
141 d->initState = InitializationState::Uninitialized;
142 setError(QGeoPositionInfoSource::UnknownSourceError);
146 d->periodicTimer.setSingleShot(
true);
147 connect(&d->periodicTimer, &QTimer::timeout,
this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate);
149 d->singleUpdateTimer.setSingleShot(
true);
150 connect(&d->singleUpdateTimer, &QTimer::timeout,
this, &QGeoPositionInfoSourceWinRT::singleUpdateTimeOut);
152 QGeoPositionInfoSource::PositioningMethods preferredMethods = preferredPositioningMethods();
153 if (preferredMethods == QGeoPositionInfoSource::NoPositioningMethods)
154 preferredMethods = QGeoPositionInfoSource::AllPositioningMethods;
155 setPreferredPositioningMethods(preferredMethods);
157 connect(
this, &QGeoPositionInfoSourceWinRT::nativePositionUpdate,
this, &QGeoPositionInfoSourceWinRT::updateSynchronized);
158 d->initState = InitializationState::Initialized;
164 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
165 Q_D(
const QGeoPositionInfoSourceWinRT);
166 Q_UNUSED(fromSatellitePositioningMethodsOnly);
167 return d->lastPosition;
172 return requestAccess() ? QGeoPositionInfoSource::AllPositioningMethods
173 : QGeoPositionInfoSource::NoPositioningMethods;
178 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << methods;
179 Q_D(QGeoPositionInfoSourceWinRT);
181 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
182 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
183 if (previousPreferredPositioningMethods == preferredPositioningMethods()
184 || d->initState == InitializationState::Uninitialized) {
188 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
193 PositionAccuracy acc = methods & PositioningMethod::SatellitePositioningMethods ?
194 PositionAccuracy::PositionAccuracy_High :
195 PositionAccuracy::PositionAccuracy_Default;
196 HRESULT hr = [d, acc]() {
197 return d->locator->put_DesiredAccuracy(acc);
199 RETURN_VOID_IF_FAILED(
"Could not set positioning accuracy.");
207 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << msec;
208 Q_D(QGeoPositionInfoSourceWinRT);
209 if (d->initState == InitializationState::Uninitialized) {
210 d->updateInterval = msec;
220 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
225 HRESULT hr = d->locator->put_ReportInterval(
static_cast<UINT32>(msec));
227 setError(QGeoPositionInfoSource::UnknownSourceError);
228 qErrnoWarning(hr,
"Failed to set update interval");
232 d->updateInterval = msec;
233 d->periodicTimer.setInterval(d->updateInterval);
235 QGeoPositionInfoSource::setUpdateInterval(d->updateInterval);
243 Q_D(
const QGeoPositionInfoSourceWinRT);
244 return d->minimumUpdateInterval == -1 ? 1000 : d->minimumUpdateInterval;
249 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
250 Q_D(QGeoPositionInfoSourceWinRT);
252 setError(QGeoPositionInfoSource::NoError);
256 if (d->updatesOngoing)
261 d->updatesOngoing =
true;
262 d->periodicTimer.start();
267 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
268 Q_D(QGeoPositionInfoSourceWinRT);
274 d->updatesOngoing =
false;
275 d->periodicTimer.stop();
280 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
281 Q_D(QGeoPositionInfoSourceWinRT);
284 if (d->positionToken.value != 0)
287 if (preferredPositioningMethods() == QGeoPositionInfoSource::NoPositioningMethods) {
288 setError(QGeoPositionInfoSource::UnknownSourceError);
293 setError(QGeoPositionInfoSource::AccessError);
297 HRESULT hr = [
this, d]() {
303 ComPtr<IAsyncOperation<Geoposition*>> op;
304 hr = d->locator->GetGeopositionAsync(&op);
305 RETURN_HR_IF_FAILED(
"Could not start position operation");
307 hr = d->locator->add_PositionChanged(Callback<GeoLocatorPositionHandler>(
this,
308 &QGeoPositionInfoSourceWinRT::onPositionChanged).Get(),
310 RETURN_HR_IF_FAILED(
"Could not add position handler");
312 hr = d->locator->add_StatusChanged(Callback<GeoLocatorStatusHandler>(
this,
313 &QGeoPositionInfoSourceWinRT::onStatusChanged).Get(),
315 RETURN_HR_IF_FAILED(
"Could not add status handler");
319 setError(QGeoPositionInfoSource::UnknownSourceError);
328 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
329 Q_D(QGeoPositionInfoSourceWinRT);
331 if (!d->positionToken.value)
333 d->locator->remove_PositionChanged(d->positionToken);
334 d->locator->remove_StatusChanged(d->statusToken);
335 d->positionToken.value = 0;
336 d->statusToken.value = 0;
341 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << timeout;
342 Q_D(QGeoPositionInfoSourceWinRT);
347 setError(QGeoPositionInfoSource::NoError);
349 d->positionError = QGeoPositionInfoSource::UpdateTimeoutError;
350 emit QGeoPositionInfoSource::errorOccurred(d->positionError);
358 d->singleUpdateTimer.start(timeout);
363 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
364 Q_D(QGeoPositionInfoSourceWinRT);
365 QMutexLocker locker(&d->mutex);
372 if (d->lastPosition.isValid()) {
373 QGeoPositionInfo sent = d->lastPosition;
374 sent.setTimestamp(sent.timestamp().addMSecs(updateInterval()));
375 d->lastPosition = sent;
376 emit positionUpdated(sent);
378 d->periodicTimer.start();
383 Q_D(QGeoPositionInfoSourceWinRT);
384 QMutexLocker locker(&d->mutex);
386 if (d->singleUpdateTimer.isActive()) {
387 d->positionError = QGeoPositionInfoSource::UpdateTimeoutError;
388 emit QGeoPositionInfoSource::errorOccurred(d->positionError);
389 if (!d->updatesOngoing)
396 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << currentInfo;
397 Q_D(QGeoPositionInfoSourceWinRT);
398 QMutexLocker locker(&d->mutex);
400 d->periodicTimer.stop();
401 d->lastPosition = currentInfo;
403 if (d->updatesOngoing)
404 d->periodicTimer.start();
406 if (d->singleUpdateTimer.isActive()) {
407 d->singleUpdateTimer.stop();
408 if (!d->updatesOngoing)
412 emit positionUpdated(currentInfo);
417 Q_D(
const QGeoPositionInfoSourceWinRT);
418 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << d->positionError;
422 if ((d->positionError == QGeoPositionInfoSource::AccessError
423 || d->positionError == QGeoPositionInfoSource::UnknownSourceError) && requestAccess())
424 return QGeoPositionInfoSource::NoError;
426 return d->positionError;
431 Q_D(QGeoPositionInfoSourceWinRT);
433 if (positionError == d->positionError)
436 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << positionError;
437 d->positionError = positionError;
438 if (positionError != QGeoPositionInfoSource::NoError)
439 emit QGeoPositionInfoSource::errorOccurred(positionError);
444 setError(positionError);
450 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
454 ComPtr<IGeoposition> position;
455 hr = args->get_Position(&position);
456 RETURN_HR_IF_FAILED(
"Could not access position object.");
458 QGeoPositionInfo currentInfo;
460 ComPtr<IGeocoordinate> coord;
461 hr = position->get_Coordinate(&coord);
463 qErrnoWarning(hr,
"Could not access coordinate");
465 ComPtr<IGeocoordinateWithPoint> pointCoordinate;
466 hr = coord.As(&pointCoordinate);
468 qErrnoWarning(hr,
"Could not cast coordinate.");
470 ComPtr<IGeopoint> point;
471 hr = pointCoordinate->get_Point(&point);
473 qErrnoWarning(hr,
"Could not obtain coordinate's point.");
475 BasicGeoposition pos;
476 hr = point->get_Position(&pos);
478 qErrnoWarning(hr,
"Could not obtain point's position.");
480 DOUBLE lat = pos.Latitude;
481 DOUBLE lon = pos.Longitude;
482 DOUBLE alt = pos.Altitude;
484 bool altitudeAvailable =
false;
485 ComPtr<IGeoshape> shape;
486 hr = point.As(&shape);
487 if (SUCCEEDED(hr) && shape) {
488 AltitudeReferenceSystem altitudeSystem;
489 hr = shape->get_AltitudeReferenceSystem(&altitudeSystem);
490 if (SUCCEEDED(hr) && altitudeSystem == AltitudeReferenceSystem_Geoid)
491 altitudeAvailable =
true;
493 if (altitudeAvailable)
494 currentInfo.setCoordinate(QGeoCoordinate(lat, lon, alt));
496 currentInfo.setCoordinate(QGeoCoordinate(lat, lon));
499 hr = coord->get_Accuracy(&accuracy);
501 currentInfo.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy);
503 IReference<
double> *altAccuracy;
504 hr = coord->get_AltitudeAccuracy(&altAccuracy);
505 if (SUCCEEDED(hr) && altAccuracy) {
507 hr = altAccuracy->get_Value(&value);
508 currentInfo.setAttribute(QGeoPositionInfo::VerticalAccuracy, value);
511 IReference<
double> *speed;
512 hr = coord->get_Speed(&speed);
513 if (SUCCEEDED(hr) && speed) {
515 hr = speed->get_Value(&value);
516 currentInfo.setAttribute(QGeoPositionInfo::GroundSpeed, value);
519 IReference<
double> *heading;
520 hr = coord->get_Heading(&heading);
521 if (SUCCEEDED(hr) && heading) {
523 hr = heading->get_Value(&value);
525 value = modf(value, &mod);
526 value +=
static_cast<
int>(mod) % 360;
527 if (value >=0 && value <= 359)
528 currentInfo.setAttribute(QGeoPositionInfo::Direction, value);
532 hr = coord->get_Timestamp(&dateTime);
534 if (dateTime.UniversalTime > 0) {
535 ULARGE_INTEGER uLarge;
536 uLarge.QuadPart = dateTime.UniversalTime;
538 fileTime.dwHighDateTime = uLarge.HighPart;
539 fileTime.dwLowDateTime = uLarge.LowPart;
540 SYSTEMTIME systemTime;
541 if (FileTimeToSystemTime(&fileTime, &systemTime)) {
542 currentInfo.setTimestamp(QDateTime(QDate(systemTime.wYear, systemTime.wMonth,
544 QTime(systemTime.wHour, systemTime.wMinute,
545 systemTime.wSecond, systemTime.wMilliseconds),
550 emit nativePositionUpdate(currentInfo);
557 return status == PositionStatus_NoData || status == PositionStatus_Disabled
558 || status == PositionStatus_NotAvailable;
563 Q_D(QGeoPositionInfoSourceWinRT);
565 const PositionStatus oldStatus = d->positionStatus;
566 HRESULT hr = args->get_Status(&d->positionStatus);
567 RETURN_HR_IF_FAILED(
"Could not obtain position status");
568 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ << d->positionStatus;
569 QGeoPositionInfoSource::Error error = QGeoPositionInfoSource::NoError;
570 switch (d->positionStatus) {
571 case PositionStatus::PositionStatus_NotAvailable:
572 error = QGeoPositionInfoSource::UnknownSourceError;
574 case PositionStatus::PositionStatus_Disabled:
575 error = QGeoPositionInfoSource::AccessError;
577 case PositionStatus::PositionStatus_NoData:
578 error = QGeoPositionInfoSource::ClosedError;
581 if (error != QGeoPositionInfoSource::NoError) {
582 QMetaObject::invokeMethod(
this,
"reactOnError", Qt::QueuedConnection,
583 Q_ARG(QGeoPositionInfoSource::Error,
584 QGeoPositionInfoSource::UnknownSourceError));
587 if (isDisabledStatus(oldStatus) != isDisabledStatus(d->positionStatus))
588 emit supportedPositioningMethodsChanged();
595 Q_D(
const QGeoPositionInfoSourceWinRT);
596 qCDebug(lcPositioningWinRT) <<
__FUNCTION__;
597 GeolocationAccessStatus accessStatus;
599 ComPtr<IAsyncOperation<GeolocationAccessStatus>> op;
600 HRESULT hr = [&op, d]() {
603 hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
604 IID_PPV_ARGS(&d->statics));
605 RETURN_HR_IF_FAILED(
"Could not access Geolocation Statics.");
608 hr = d->statics->RequestAccessAsync(&op);
612 qCDebug(lcPositioningWinRT) <<
__FUNCTION__ <<
"Requesting access from Xaml thread failed";
616 await(op, &accessStatus);
617 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)