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
qgeopositioninfosource_winrt.cpp
Go to the documentation of this file.
1// Copyright (C) 2015 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 <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>
12
13#include <functional>
14#include <windows.system.h>
15#include <windows.devices.geolocation.h>
16#include <windows.foundation.h>
17#include <windows.foundation.collections.h>
18
19using namespace Microsoft::WRL;
20using namespace Microsoft::WRL::Wrappers;
22using namespace ABI::Windows::Foundation;
23using namespace ABI::Windows::Foundation::Collections;
24
25typedef ITypedEventHandler<Geolocator *, PositionChangedEventArgs *> GeoLocatorPositionHandler;
26typedef ITypedEventHandler<Geolocator *, StatusChangedEventArgs *> GeoLocatorStatusHandler;
27typedef IAsyncOperationCompletedHandler<Geoposition*> PositionHandler;
28typedef IAsyncOperationCompletedHandler<GeolocationAccessStatus> AccessHandler;
29
30Q_DECLARE_LOGGING_CATEGORY(lcPositioningWinRT)
31
33
35
36static inline HRESULT await(const ComPtr<IAsyncOperation<GeolocationAccessStatus>> &asyncOp,
37 GeolocationAccessStatus *result)
38{
39 ComPtr<IAsyncInfo> asyncInfo;
40 HRESULT hr = asyncOp.As(&asyncInfo);
41 if (FAILED(hr))
42 return hr;
43
44 AsyncStatus status;
45 while (SUCCEEDED(hr = asyncInfo->get_Status(&status)) && status == AsyncStatus::Started)
47
48 if (FAILED(hr) || status != AsyncStatus::Completed) {
49 HRESULT ec;
50 hr = asyncInfo->get_ErrorCode(&ec);
51 if (FAILED(hr))
52 return hr;
53 hr = asyncInfo->Close();
54 if (FAILED(hr))
55 return hr;
56 return ec;
57 }
58
59 if (FAILED(hr))
60 return hr;
61
62 return asyncOp->GetResults(result);
63}
64
70
89
93{
94 qRegisterMetaType<QGeoPositionInfoSource::Error>();
95 qCDebug(lcPositioningWinRT) << __FUNCTION__;
96 CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
98 d->positionError = QGeoPositionInfoSource::NoError;
99 d->updatesOngoing = false;
100 d->positionToken.value = 0;
101 d->statusToken.value = 0;
102}
103
105{
106 qCDebug(lcPositioningWinRT) << __FUNCTION__;
107 CoUninitialize();
108}
109
111{
114 if (d->initState == InitializationState::Initialized)
115 return 0;
116
117 qCDebug(lcPositioningWinRT) << __FUNCTION__;
119 if (!requestAccess()) {
122 qWarning ("Location access failed.");
123 return -1;
124 }
125 HRESULT hr = [this, d]() {
126 HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Devices_Geolocation_Geolocator).Get(),
127 &d->locator);
128 RETURN_HR_IF_FAILED("Could not initialize native location services.");
129
130 if (d->minimumUpdateInterval == -1) {
131 UINT32 interval;
132 hr = d->locator->get_ReportInterval(&interval);
133 RETURN_HR_IF_FAILED("Could not retrieve report interval.");
134 d->minimumUpdateInterval = static_cast<int>(interval);
135 }
136 if (d->updateInterval == -1)
137 d->updateInterval = d->minimumUpdateInterval;
138 setUpdateInterval(d->updateInterval);
139
140 return hr;
141 }();
142 if (FAILED(hr)) {
145 return -1;
146 }
147
148 d->periodicTimer.setSingleShot(true);
149 connect(&d->periodicTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate);
150
151 d->singleUpdateTimer.setSingleShot(true);
152 connect(&d->singleUpdateTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::singleUpdateTimeOut);
153
154 QGeoPositionInfoSource::PositioningMethods preferredMethods = preferredPositioningMethods();
155 if (preferredMethods == QGeoPositionInfoSource::NoPositioningMethods)
157 setPreferredPositioningMethods(preferredMethods);
158
159 connect(this, &QGeoPositionInfoSourceWinRT::nativePositionUpdate, this, &QGeoPositionInfoSourceWinRT::updateSynchronized);
161 return 0;
162}
163
164QGeoPositionInfo QGeoPositionInfoSourceWinRT::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
165{
166 qCDebug(lcPositioningWinRT) << __FUNCTION__;
168 Q_UNUSED(fromSatellitePositioningMethodsOnly);
169 return d->lastPosition;
170}
171
177
178void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInfoSource::PositioningMethods methods)
179{
180 qCDebug(lcPositioningWinRT) << __FUNCTION__ << methods;
182
183 PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
185 if (previousPreferredPositioningMethods == preferredPositioningMethods()
186 || d->initState == InitializationState::Uninitialized) {
187 return;
188 }
189
190 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
191
192 if (needsRestart)
193 stopHandler();
194
196 PositionAccuracy::PositionAccuracy_High :
197 PositionAccuracy::PositionAccuracy_Default;
198 HRESULT hr = [d, acc]() {
199 return d->locator->put_DesiredAccuracy(acc);
200 }();
201 RETURN_VOID_IF_FAILED("Could not set positioning accuracy.");
202
203 if (needsRestart)
204 startHandler();
205}
206
208{
209 qCDebug(lcPositioningWinRT) << __FUNCTION__ << msec;
211 if (d->initState == InitializationState::Uninitialized) {
212 d->updateInterval = msec;
213 return;
214 }
215
216 // minimumUpdateInterval is initialized to the lowest possible update interval in init().
217 // Passing 0 will cause an error on Windows 10.
218 // See https://docs.microsoft.com/en-us/uwp/api/windows.devices.geolocation.geolocator.reportinterval
219 if (msec < minimumUpdateInterval())
220 msec = minimumUpdateInterval();
221
222 const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
223
224 if (needsRestart)
225 stopHandler();
226
227 HRESULT hr = d->locator->put_ReportInterval(static_cast<UINT32>(msec));
228 if (FAILED(hr)) {
230 qErrnoWarning(hr, "Failed to set update interval");
231 return;
232 }
233
234 d->updateInterval = msec;
235 d->periodicTimer.setInterval(d->updateInterval);
236
238
239 if (needsRestart)
240 startHandler();
241}
242
244{
246 return d->minimumUpdateInterval == -1 ? 1000 : d->minimumUpdateInterval;
247}
248
250{
251 qCDebug(lcPositioningWinRT) << __FUNCTION__;
253
255 if (init() < 0)
256 return;
257
258 if (d->updatesOngoing)
259 return;
260
261 if (!startHandler())
262 return;
263 d->updatesOngoing = true;
264 d->periodicTimer.start();
265}
266
268{
269 qCDebug(lcPositioningWinRT) << __FUNCTION__;
271
272 if (init() < 0)
273 return;
274
275 stopHandler();
276 d->updatesOngoing = false;
277 d->periodicTimer.stop();
278}
279
280bool QGeoPositionInfoSourceWinRT::startHandler()
281{
282 qCDebug(lcPositioningWinRT) << __FUNCTION__;
284
285 // Check if already attached
286 if (d->positionToken.value != 0)
287 return true;
288
291 return false;
292 }
293
294 if (!requestAccess()) {
296 return false;
297 }
298
299 HRESULT hr = [this, d]() {
300 HRESULT hr;
301
302 // We need to call this at least once on Windows 10 Mobile.
303 // Unfortunately this operation does not have a completion handler
304 // registered. That could have helped in the single update case
305 ComPtr<IAsyncOperation<Geoposition*>> op;
306 hr = d->locator->GetGeopositionAsync(&op);
307 RETURN_HR_IF_FAILED("Could not start position operation");
308
309 hr = d->locator->add_PositionChanged(Callback<GeoLocatorPositionHandler>(this,
311 &d->positionToken);
312 RETURN_HR_IF_FAILED("Could not add position handler");
313
314 hr = d->locator->add_StatusChanged(Callback<GeoLocatorStatusHandler>(this,
316 &d->statusToken);
317 RETURN_HR_IF_FAILED("Could not add status handler");
318 return hr;
319 }();
320 if (FAILED(hr)) {
322 return false;
323 }
324
325 return true;
326}
327
328void QGeoPositionInfoSourceWinRT::stopHandler()
329{
330 qCDebug(lcPositioningWinRT) << __FUNCTION__;
332
333 if (!d->positionToken.value)
334 return;
335 d->locator->remove_PositionChanged(d->positionToken);
336 d->locator->remove_StatusChanged(d->statusToken);
337 d->positionToken.value = 0;
338 d->statusToken.value = 0;
339}
340
342{
343 qCDebug(lcPositioningWinRT) << __FUNCTION__ << timeout;
345
346 if (init() < 0)
347 return;
348
350 if (timeout != 0 && timeout < minimumUpdateInterval()) {
353 return;
354 }
355
356 if (timeout == 0)
357 timeout = 2*60*1000; // Maximum time for cold start (see Android)
358
359 if (startHandler())
360 d->singleUpdateTimer.start(timeout);
361}
362
363void QGeoPositionInfoSourceWinRT::virtualPositionUpdate()
364{
365 qCDebug(lcPositioningWinRT) << __FUNCTION__;
367 QMutexLocker locker(&d->mutex);
368
369 // The operating system did not provide information in time
370 // Hence we send a virtual position update to keep same behavior
371 // between backends.
372 // This only applies to the periodic timer, not for single requests
373 // We can only do this if we received a valid position before
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);
379 }
380 d->periodicTimer.start();
381}
382
383void QGeoPositionInfoSourceWinRT::singleUpdateTimeOut()
384{
386 QMutexLocker locker(&d->mutex);
387
388 if (d->singleUpdateTimer.isActive()) {
391 if (!d->updatesOngoing)
392 stopHandler();
393 }
394}
395
396void QGeoPositionInfoSourceWinRT::updateSynchronized(QGeoPositionInfo currentInfo)
397{
398 qCDebug(lcPositioningWinRT) << __FUNCTION__ << currentInfo;
400 QMutexLocker locker(&d->mutex);
401
402 d->periodicTimer.stop();
403 d->lastPosition = currentInfo;
404
405 if (d->updatesOngoing)
406 d->periodicTimer.start();
407
408 if (d->singleUpdateTimer.isActive()) {
409 d->singleUpdateTimer.stop();
410 if (!d->updatesOngoing)
411 stopHandler();
412 }
413
414 emit positionUpdated(currentInfo);
415}
416
418{
420 qCDebug(lcPositioningWinRT) << __FUNCTION__ << d->positionError;
421
422 // If the last encountered error was "Access denied", it is possible that the location service
423 // has been enabled by now so that we are clear again.
424 if ((d->positionError == QGeoPositionInfoSource::AccessError
427
428 return d->positionError;
429}
430
431void QGeoPositionInfoSourceWinRT::setError(QGeoPositionInfoSource::Error positionError)
432{
434
435 if (positionError == d->positionError)
436 return;
437
438 qCDebug(lcPositioningWinRT) << __FUNCTION__ << positionError;
439 d->positionError = positionError;
440 if (positionError != QGeoPositionInfoSource::NoError)
442}
443
444void QGeoPositionInfoSourceWinRT::reactOnError(QGeoPositionInfoSource::Error positionError)
445{
446 setError(positionError);
447 stopUpdates();
448}
449
450HRESULT QGeoPositionInfoSourceWinRT::onPositionChanged(IGeolocator *locator, IPositionChangedEventArgs *args)
451{
452 qCDebug(lcPositioningWinRT) << __FUNCTION__;
453 Q_UNUSED(locator);
454
455 HRESULT hr;
456 ComPtr<IGeoposition> position;
457 hr = args->get_Position(&position);
458 RETURN_HR_IF_FAILED("Could not access position object.");
459
460 QGeoPositionInfo currentInfo;
461
462 ComPtr<IGeocoordinate> coord;
463 hr = position->get_Coordinate(&coord);
464 if (FAILED(hr))
465 qErrnoWarning(hr, "Could not access coordinate");
466
467 ComPtr<IGeocoordinateWithPoint> pointCoordinate;
468 hr = coord.As(&pointCoordinate);
469 if (FAILED(hr))
470 qErrnoWarning(hr, "Could not cast coordinate.");
471
472 ComPtr<IGeopoint> point;
473 hr = pointCoordinate->get_Point(&point);
474 if (FAILED(hr))
475 qErrnoWarning(hr, "Could not obtain coordinate's point.");
476
477 BasicGeoposition pos;
478 hr = point->get_Position(&pos);
479 if (FAILED(hr))
480 qErrnoWarning(hr, "Could not obtain point's position.");
481
482 DOUBLE lat = pos.Latitude;
483 DOUBLE lon = pos.Longitude;
484 DOUBLE alt = pos.Altitude;
485
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;
494 }
495 if (altitudeAvailable)
496 currentInfo.setCoordinate(QGeoCoordinate(lat, lon, alt));
497 else
498 currentInfo.setCoordinate(QGeoCoordinate(lat, lon));
499
500 DOUBLE accuracy;
501 hr = coord->get_Accuracy(&accuracy);
502 if (SUCCEEDED(hr))
503 currentInfo.setAttribute(QGeoPositionInfo::HorizontalAccuracy, accuracy);
504
505 IReference<double> *altAccuracy;
506 hr = coord->get_AltitudeAccuracy(&altAccuracy);
507 if (SUCCEEDED(hr) && altAccuracy) {
508 double value;
509 hr = altAccuracy->get_Value(&value);
510 currentInfo.setAttribute(QGeoPositionInfo::VerticalAccuracy, value);
511 }
512
513 IReference<double> *speed;
514 hr = coord->get_Speed(&speed);
515 if (SUCCEEDED(hr) && speed) {
516 double value;
517 hr = speed->get_Value(&value);
518 currentInfo.setAttribute(QGeoPositionInfo::GroundSpeed, value);
519 }
520
521 IReference<double> *heading;
522 hr = coord->get_Heading(&heading);
523 if (SUCCEEDED(hr) && heading) {
524 double value;
525 hr = heading->get_Value(&value);
526 double mod = 0;
527 value = modf(value, &mod);
528 value += static_cast<int>(mod) % 360;
529 if (value >=0 && value <= 359) // get_Value might return nan/-nan
530 currentInfo.setAttribute(QGeoPositionInfo::Direction, value);
531 }
532
533 DateTime dateTime;
534 hr = coord->get_Timestamp(&dateTime);
535
536 if (dateTime.UniversalTime > 0) {
537 ULARGE_INTEGER uLarge;
538 uLarge.QuadPart = dateTime.UniversalTime;
539 FILETIME fileTime;
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,
545 systemTime.wDay),
546 QTime(systemTime.wHour, systemTime.wMinute,
547 systemTime.wSecond, systemTime.wMilliseconds),
549 }
550 }
551
552 emit nativePositionUpdate(currentInfo);
553
554 return S_OK;
555}
556
557static inline bool isDisabledStatus(PositionStatus status)
558{
559 return status == PositionStatus_NoData || status == PositionStatus_Disabled
560 || status == PositionStatus_NotAvailable;
561}
562
563HRESULT QGeoPositionInfoSourceWinRT::onStatusChanged(IGeolocator *, IStatusChangedEventArgs *args)
564{
566
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;
572 switch (d->positionStatus) {
573 case PositionStatus::PositionStatus_NotAvailable:
575 break;
576 case PositionStatus::PositionStatus_Disabled:
578 break;
579 case PositionStatus::PositionStatus_NoData:
581 break;
582 }
587 }
588
589 if (isDisabledStatus(oldStatus) != isDisabledStatus(d->positionStatus))
591
592 return S_OK;
593}
594
596{
598 qCDebug(lcPositioningWinRT) << __FUNCTION__;
599 GeolocationAccessStatus accessStatus;
600
601 ComPtr<IAsyncOperation<GeolocationAccessStatus>> op;
602 HRESULT hr = [&op, d]() {
603 HRESULT hr;
604 if (!d->statics) {
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.");
608 }
609
610 hr = d->statics->RequestAccessAsync(&op);
611 return hr;
612 }();
613 if (FAILED(hr)) {
614 qCDebug(lcPositioningWinRT) << __FUNCTION__ << "Requesting access from Xaml thread failed";
615 return false;
616 }
617
618 await(op, &accessStatus);
619 return accessStatus == GeolocationAccessStatus_Allowed;
620}
621
static JNINativeMethod methods[]
\inmodule QtCore\reentrant
Definition qdatetime.h:283
\inmodule QtCore \reentrant
Definition qdatetime.h:29
\inmodule QtPositioning
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.
Error error() const override
Returns the type of error that last occurred.
void nativePositionUpdate(const QGeoPositionInfo)
QGeoPositionInfoSourceWinRT(QObject *parent=nullptr)
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
PositioningMethods supportedPositioningMethods() const override
Returns the positioning methods available to this source.
\inmodule QtPositioning
int updateInterval
This property holds the requested interval in milliseconds between each update.
void positionUpdated(const QGeoPositionInfo &update)
If startUpdates() or requestUpdate() is called, this signal is emitted when an update becomes availab...
void errorOccurred(QGeoPositionInfoSource::Error)
This signal is emitted after an error occurred.
virtual void setPreferredPositioningMethods(PositioningMethods methods)
void supportedPositioningMethodsChanged()
This signal is emitted when the supported positioning methods changed.
Error
The Error enumeration represents the errors which can occur.
virtual void setUpdateInterval(int msec)
PositioningMethods preferredPositioningMethods
Sets the preferred positioning methods for this source.
\inmodule QtPositioning
void setTimestamp(const QDateTime &timestamp)
Sets the date and time at which this position was reported to timestamp.
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
\inmodule QtCore
Definition qobject.h:103
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType=Qt::AutoConnection)
\threadsafe
Definition qobject.cpp:2960
static void yieldCurrentThread()
Definition qthread.cpp:1054
\inmodule QtCore \reentrant
Definition qdatetime.h:215
\inmodule QtCore
Definition qtimer.h:20
void timeout(QPrivateSignal)
This signal is emitted when the timer times out.
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
@ QueuedConnection
static QT_BEGIN_NAMESPACE bool await(IAsyncOperation< T > &&asyncInfo, T &result, uint timeout=0)
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
IAsyncOperationCompletedHandler< Geoposition * > PositionHandler
ITypedEventHandler< Geolocator *, StatusChangedEventArgs * > GeoLocatorStatusHandler
static QT_BEGIN_NAMESPACE HRESULT await(const ComPtr< IAsyncOperation< GeolocationAccessStatus > > &asyncOp, GeolocationAccessStatus *result)
IAsyncOperationCompletedHandler< GeolocationAccessStatus > AccessHandler
ITypedEventHandler< Geolocator *, PositionChangedEventArgs * > GeoLocatorPositionHandler
static bool isDisabledStatus(PositionStatus status)
#define qWarning
Definition qlogging.h:166
#define qCDebug(category,...)
#define Q_DECLARE_LOGGING_CATEGORY(name)
#define Q_DECLARE_METATYPE(TYPE)
Definition qmetatype.h:1525
#define Q_ARG(Type, data)
Definition qobjectdefs.h:63
GLbitfield GLuint64 timeout
[4]
GLuint coord
GLuint64EXT * result
[6]
static qreal position(const QQuickItem *item, QQuickAnchors::Anchor anchorLine)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define emit
#define Q_UNUSED(x)
long HRESULT
QDateTime dateTime
[12]
QJSValueList args
static bool invokeMethod(QObject *obj, const char *member, Qt::ConnectionType, QGenericReturnArgument ret, QGenericArgument val0=QGenericArgument(nullptr), QGenericArgument val1=QGenericArgument(), QGenericArgument val2=QGenericArgument(), QGenericArgument val3=QGenericArgument(), QGenericArgument val4=QGenericArgument(), QGenericArgument val5=QGenericArgument(), QGenericArgument val6=QGenericArgument(), QGenericArgument val7=QGenericArgument(), QGenericArgument val8=QGenericArgument(), QGenericArgument val9=QGenericArgument())
\threadsafe This is an overloaded member function, provided for convenience. It differs from the abov...