From b17c07dc0b947c979372fbaf558ce9acc07d1837 Mon Sep 17 00:00:00 2001
From: Oliver Wolff <oliver.wolff@qt.io>
Date: Fri, 4 Jan 2019 12:20:44 +0100
Subject: [PATCH] winrt: Initialize position source on first usage

Loading the plugin without access to the location service will
work and not error out. It is still possible to enable location service
later and the backend will work as expected.

This patch changes the behavior so that the pop up asking for access to
the user's location data is no longer shown when the plugin is loaded, but
when the backend is actually used for the first time.

Change-Id: I23100f7867610c6f23b2d2ea5c15c268468949a9
Reviewed-by: Miguel Costa <miguel.costa@qt.io>
---
 .../winrt/qgeopositioninfosource_winrt.cpp    | 72 ++++++++++++++-----
 .../qgeopositioninfosourcefactory_winrt.cpp   |  6 --
 2 files changed, 54 insertions(+), 24 deletions(-)

diff --git a/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp b/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp
index 2fedf5b5a..fe783bd2b 100644
--- a/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp
+++ b/src/plugins/position/winrt/qgeopositioninfosource_winrt.cpp
@@ -106,6 +106,12 @@ static inline HRESULT await(const ComPtr<IAsyncOperation<GeolocationAccessStatus
 }
 #endif // !Q_OS_WINRT
 
+enum class InitializationState {
+    Uninitialized,
+    Initializing,
+    Initialized
+};
+
 class QGeoPositionInfoSourceWinRTPrivate {
 public:
     ComPtr<IGeolocator> locator;
@@ -118,6 +124,8 @@ public:
     QMutex mutex;
     bool updatesOngoing = false;
     int minimumUpdateInterval = -1;
+    int updateInterval = -1;
+    InitializationState initState = InitializationState::Uninitialized;
 
     PositionStatus positionStatus = PositionStatus_NotInitialized;
 };
@@ -144,9 +152,16 @@ QGeoPositionInfoSourceWinRT::~QGeoPositionInfoSourceWinRT()
 
 int QGeoPositionInfoSourceWinRT::init()
 {
-    qCDebug(lcPositioningWinRT) << __FUNCTION__;
     Q_D(QGeoPositionInfoSourceWinRT);
+    Q_ASSERT(d->initState != InitializationState::Initializing);
+    if (d->initState == InitializationState::Initialized)
+        return 0;
+
+    qCDebug(lcPositioningWinRT) << __FUNCTION__;
+    d->initState = InitializationState::Initializing;
     if (!requestAccess()) {
+        d->initState = InitializationState::Uninitialized;
+        setError(QGeoPositionInfoSource::AccessError);
         qWarning ("Location access failed.");
         return -1;
     }
@@ -155,35 +170,37 @@ int QGeoPositionInfoSourceWinRT::init()
                                         &d->locator);
         RETURN_HR_IF_FAILED("Could not initialize native location services.");
 
-        UINT32 interval;
-        hr = d->locator->get_ReportInterval(&interval);
-        RETURN_HR_IF_FAILED("Could not retrieve report interval.");
-        d->minimumUpdateInterval = static_cast<int>(interval);
-        setUpdateInterval(d->minimumUpdateInterval);
+        if (d->minimumUpdateInterval == -1) {
+            UINT32 interval;
+            hr = d->locator->get_ReportInterval(&interval);
+            RETURN_HR_IF_FAILED("Could not retrieve report interval.");
+            d->minimumUpdateInterval = static_cast<int>(interval);
+        }
+        if (d->updateInterval == -1)
+            d->updateInterval = d->minimumUpdateInterval;
+        setUpdateInterval(d->updateInterval);
 
         return hr;
     });
     if (FAILED(hr)) {
+        d->initState = InitializationState::Uninitialized;
         setError(QGeoPositionInfoSource::UnknownSourceError);
         return -1;
     }
 
-    hr = d->locator->put_DesiredAccuracy(PositionAccuracy::PositionAccuracy_Default);
-    if (FAILED(hr)) {
-        setError(QGeoPositionInfoSource::UnknownSourceError);
-        qErrnoWarning(hr, "Could not initialize desired accuracy.");
-        return -1;
-    }
-
     d->periodicTimer.setSingleShot(true);
     connect(&d->periodicTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::virtualPositionUpdate);
 
     d->singleUpdateTimer.setSingleShot(true);
     connect(&d->singleUpdateTimer, &QTimer::timeout, this, &QGeoPositionInfoSourceWinRT::singleUpdateTimeOut);
 
-    setPreferredPositioningMethods(QGeoPositionInfoSource::AllPositioningMethods);
+    QGeoPositionInfoSource::PositioningMethods preferredMethods = preferredPositioningMethods();
+    if (preferredMethods == QGeoPositionInfoSource::NoPositioningMethods)
+        preferredMethods = QGeoPositionInfoSource::AllPositioningMethods;
+    setPreferredPositioningMethods(preferredMethods);
 
     connect(this, &QGeoPositionInfoSourceWinRT::nativePositionUpdate, this, &QGeoPositionInfoSourceWinRT::updateSynchronized);
+    d->initState = InitializationState::Initialized;
     return 0;
 }
 
@@ -208,9 +225,12 @@ void QGeoPositionInfoSourceWinRT::setPreferredPositioningMethods(QGeoPositionInf
 
     PositioningMethods previousPreferredPositioningMethods = preferredPositioningMethods();
     QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
-    if (previousPreferredPositioningMethods == preferredPositioningMethods())
+    if (previousPreferredPositioningMethods == preferredPositioningMethods()
+            || d->initState != InitializationState::Uninitialized) {
         return;
+    }
 
+    Q_ASSERT(d->initState != InitializationState::Initializing);
     const bool needsRestart = d->positionToken.value != 0 || d->statusToken.value != 0;
 
     if (needsRestart)
@@ -232,6 +252,12 @@ void QGeoPositionInfoSourceWinRT::setUpdateInterval(int msec)
 {
     qCDebug(lcPositioningWinRT) << __FUNCTION__ << msec;
     Q_D(QGeoPositionInfoSourceWinRT);
+    if (d->initState == InitializationState::Uninitialized) {
+        d->updateInterval = msec;
+        return;
+    }
+
+    Q_ASSERT(d->initState != InitializationState::Initializing);
     // minimumUpdateInterval is initialized to the lowest possible update interval in init().
     // Passing 0 will cause an error on Windows 10.
     // See https://docs.microsoft.com/en-us/uwp/api/windows.devices.geolocation.geolocator.reportinterval
@@ -250,9 +276,10 @@ void QGeoPositionInfoSourceWinRT::setUpdateInterval(int msec)
         return;
     }
 
-    d->periodicTimer.setInterval(qMax(msec, minimumUpdateInterval()));
+    d->updateInterval = msec;
+    d->periodicTimer.setInterval(d->updateInterval);
 
-    QGeoPositionInfoSource::setUpdateInterval(msec);
+    QGeoPositionInfoSource::setUpdateInterval(d->updateInterval);
 
     if (needsRestart)
         startHandler();
@@ -261,7 +288,7 @@ void QGeoPositionInfoSourceWinRT::setUpdateInterval(int msec)
 int QGeoPositionInfoSourceWinRT::minimumUpdateInterval() const
 {
     Q_D(const QGeoPositionInfoSourceWinRT);
-    return d->minimumUpdateInterval;
+    return d->minimumUpdateInterval == -1 ? 1000 : d->minimumUpdateInterval;
 }
 
 void QGeoPositionInfoSourceWinRT::startUpdates()
@@ -270,6 +297,9 @@ void QGeoPositionInfoSourceWinRT::startUpdates()
     Q_D(QGeoPositionInfoSourceWinRT);
 
     setError(QGeoPositionInfoSource::NoError);
+    if (init() < 0)
+        return;
+
     if (d->updatesOngoing)
         return;
 
@@ -284,6 +314,9 @@ void QGeoPositionInfoSourceWinRT::stopUpdates()
     qCDebug(lcPositioningWinRT) << __FUNCTION__;
     Q_D(QGeoPositionInfoSourceWinRT);
 
+    if (init() < 0)
+        return;
+
     stopHandler();
     d->updatesOngoing = false;
     d->periodicTimer.stop();
@@ -358,6 +391,9 @@ void QGeoPositionInfoSourceWinRT::requestUpdate(int timeout)
     qCDebug(lcPositioningWinRT) << __FUNCTION__ << timeout;
     Q_D(QGeoPositionInfoSourceWinRT);
 
+    if (init() < 0)
+        return;
+
     setError(QGeoPositionInfoSource::NoError);
     if (timeout != 0 && timeout < minimumUpdateInterval()) {
         emit updateTimeout();
diff --git a/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp
index b1ec6fb37..03b86ca77 100644
--- a/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp
+++ b/src/plugins/position/winrt/qgeopositioninfosourcefactory_winrt.cpp
@@ -47,12 +47,6 @@ QGeoPositionInfoSource *QGeoPositionInfoSourceFactoryWinRT::positionInfoSource(Q
 {
     qCDebug(lcPositioningWinRT) << __FUNCTION__;
     QGeoPositionInfoSourceWinRT *src = new QGeoPositionInfoSourceWinRT(parent);
-    if (src->init() < 0) {
-        qCDebug(lcPositioningWinRT) << __FUNCTION__ << "Source initialization failed.";
-        delete src;
-        return nullptr;
-    }
-    qCDebug(lcPositioningWinRT) << __FUNCTION__ << "Created position info source.";
     return src;
 }
 
-- 
GitLab