Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qohosgeopositioninfosource.cpp
Go to the documentation of this file.
1// Copyright (C) 2025 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#include "qohosenums.h"
7#include <QtCore/private/qcore_ohos_p.h>
8#include <QtCore/private/qohoscommon_p.h>
9#include <QtCore/private/qohosjstools_p.h>
10#include <QtCore/private/qohoslogger_p.h>
11#include <QtCore/qtimer.h>
12#include <algorithm>
13#include <cmath>
14#include <cstdlib>
15#include <functional>
16#include <memory>
17#include <optional>
18#include <utility>
19
20QT_BEGIN_NAMESPACE
21
22namespace {
23
26
27constexpr int coldStartMarginMs = 2 * 60 * 1000;
28constexpr int continuousUpdateTimerIntervalMs = 30 * 1000;
29
31{
32 switch (scenario) {
34 return "NAVIGATION";
36 return "SPORT";
38 return "TRANSPORT";
40 return "DAILY_LIFE_SERVICE";
41 }
42
43 qOhosReportFatalErrorAndAbort(
44 "%s received invalid UserActivityScenario: %d", Q_FUNC_INFO, scenario);
45}
46
47const char *toStaticString(LocationError locationError)
48{
49 switch (locationError) {
51 return "LOCATING_FAILED_DEFAULT";
53 return "LOCATING_FAILED_LOCATION_PERMISSION_DENIED";
55 return "LOCATING_FAILED_BACKGROUND_PERMISSION_DENIED";
57 return "LOCATING_FAILED_LOCATION_SWITCH_OFF";
59 return "LOCATING_FAILED_INTERNET_ACCESS_FAILURE";
60 }
61
62 qOhosReportFatalErrorAndAbort(
63 "%s received invalid LocationError: %d", Q_FUNC_INFO, locationError);
64}
65
67 QGeoPositionInfoSource::PositioningMethods positioningMethods)
68{
69 qOhosPrintfDebug("%s: %d", Q_FUNC_INFO, static_cast<int>(positioningMethods));
70
71 if (positioningMethods.testFlag(QGeoPositionInfoSource::SatellitePositioningMethods))
73
74 if (positioningMethods.testFlag(QGeoPositionInfoSource::NonSatellitePositioningMethods))
76
77 return std::nullopt;
78}
79
80QGeoPositionInfo convertLocationObjectToPositionInfo(const QNapi::Object &locationObject)
81{
82 auto dateTime = QDateTime::fromMSecsSinceEpoch(
83 locationObject.get<QNapi::Number>("timeStamp").Int64Value());
84 QGeoCoordinate coordinate(
85 locationObject.get<QNapi::Number>("latitude"),
86 locationObject.get<QNapi::Number>("longitude"),
87 locationObject.get<QNapi::Number>("altitude"));
88
89 QGeoPositionInfo positionInfo(coordinate, dateTime);
90
91 positionInfo.setAttribute(
92 QGeoPositionInfo::Direction, locationObject.get<QNapi::Number>("direction"));
93 positionInfo.setAttribute(
94 QGeoPositionInfo::HorizontalAccuracy, locationObject.get<QNapi::Number>("accuracy"));
95 positionInfo.setAttribute(
96 QGeoPositionInfo::VerticalAccuracy, locationObject.get<QNapi::Number>("altitudeAccuracy"));
97 // NOTE: QGeoPositionInfo distinguishes GroundSpeed and VerticalSpeed, while OHOS provides only
98 // Location_BasicInfo::speed. Here, we assume, that since Location_BasicInfo::accuracy relates
99 // to HorizontalAccuracy, the Location_BasicInfo::speed relates to GroundSpeed (although, there
100 // is no information about that anywhere in the documentation).
101 positionInfo.setAttribute(
102 QGeoPositionInfo::GroundSpeed, locationObject.get<QNapi::Number>("speed"));
103
104 return positionInfo;
105}
106
108 QObject *contextObject, long intervalSec,
109 UserActivityScenario userActivityScenario,
110 QOhosConsumer<const QGeoPositionInfo &> positionInfoUpdateConsumer)
111{
112 auto sharedPositionInfoUpdateConsumer = QtOhos::moveToSharedPtr(std::move(positionInfoUpdateConsumer));
113
114 auto weakPositionInfoUpdateConsumer = QtOhos::makeWeakPtr(sharedPositionInfoUpdateConsumer);
115 auto contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject);
116
117 auto registrationHandle = QOhosJsThreadGateway::eval(
118 [&](QOhosJsState &jsState) {
119 qOhosPrintfWarning("%s Interval in Seconds: %li", Q_FUNC_INFO, intervalSec);
120 auto continuousLocationRequest = QNapi::makeObject(
121 jsState.env(),
122 {
123 {"interval", intervalSec},
124 {"locationScenario", jsState.mapOhosEnumToJs(userActivityScenario)},
125 });
126
127 qOhosPrintfDebug(
128 "%s registering callback with scenario: %s",
129 Q_FUNC_INFO, toStaticString(userActivityScenario));
130
131 return QtOhos::makeProxyWithJsThreadDeleter(
132 registerQOhosOnOffMethodsBasedEventHandler(
133 getGeoLocationManagerObject(jsState), "locationChange",
134 [contextObjectRef, weakPositionInfoUpdateConsumer](const QOhosCallbackInfo &cbInfo) {
135 auto location = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
136 auto positionInfo = convertLocationObjectToPositionInfo(location);
137
138 contextObjectRef.visitInQtThreadIfAlive(
139 [weakPositionInfoUpdateConsumer, positionInfo](auto &) {
140 auto sharedPositionInfoUpdateConsumer = weakPositionInfoUpdateConsumer.lock();
141 if (sharedPositionInfoUpdateConsumer)
142 (*sharedPositionInfoUpdateConsumer)(positionInfo);
143 });
144 },
145 {
146 .extraOnArg = std::make_optional<QNapi::ValueWrapper>(continuousLocationRequest),
147 }));
148 });
149
150 return QtOhos::moveToSharedPtr(
151 std::make_pair(sharedPositionInfoUpdateConsumer, registrationHandle));
152}
153
155 QObject *contextObject, int intervalMs, QGeoPositionInfoSource::PositioningMethods positioningMethods,
156 QOhosConsumer<const QGeoPositionInfo &, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer)
157{
158 auto userActivityScenario =
159 tryMapPositioningMethodsToUserActivityScenario(positioningMethods)
160 .value_or(UserActivityScenario::DAILY_LIFE_SERVICE);
161 auto intervalSec = std::lround(static_cast<double>(intervalMs) / 1000.0);
162
163 return registerLocationChangeUpdateHandler(
164 contextObject, intervalSec, userActivityScenario,
165 [positionInfoUpdateConsumer = std::move(positionInfoUpdateConsumer), positioningMethods](const QGeoPositionInfo &positionInfo) {
166 positionInfoUpdateConsumer(positionInfo, positioningMethods);
167 });
168}
169
171 QObject *contextObject, int timeoutMs, QGeoPositionInfoSource::PositioningMethods positioningMethods,
172 QOhosConsumer<std::optional<QGeoPositionInfo>, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer)
173{
174 qOhosDebug(QtForOhos) << Q_FUNC_INFO << contextObject << timeoutMs;
175
176 struct Context
177 {
178 QOhosConsumer<std::optional<QGeoPositionInfo>, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer;
179 QtOhos::QThreadSafeRef<QObject> contextObjectRef;
180 };
181
182 auto sharedContext = QtOhos::moveToSharedPtr(Context{
183 .positionInfoUpdateConsumer = std::move(positionInfoUpdateConsumer),
184 .contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject)
185 });
186
187 auto weakContext = QtOhos::makeWeakPtr(sharedContext);
188 auto positionInfoUpdateConsumerJsProxy =
189 [weakContext, positioningMethods](std::optional<QGeoPositionInfo> optPositionInfo) {
190 auto sharedContext = weakContext.lock();
191 if (sharedContext) {
192 sharedContext->contextObjectRef.visitInQtThreadIfAlive(
193 [weakContext, optPositionInfo, positioningMethods](auto &) {
194 auto sharedContext = weakContext.lock();
195 if (sharedContext)
196 sharedContext->positionInfoUpdateConsumer(optPositionInfo, positioningMethods);
197 });
198 }
199 };
200
201 QOhosJsThreadGateway::runAndWait(
202 [&](QOhosJsState &jsState) {
203 auto userActivityScenario =
204 tryMapPositioningMethodsToUserActivityScenario(positioningMethods)
205 .value_or(UserActivityScenario::DAILY_LIFE_SERVICE);
206 auto currentLocationRequest = QNapi::makeObject(
207 jsState.env(),
208 {
209 {"scenario", jsState.mapOhosEnumToJs(userActivityScenario)},
210 {"timeoutMs", timeoutMs},
211 });
212
213 getGeoLocationManagerObject(jsState)
214 .evalToPromiseOrRejectOnThrow("getCurrentLocation(*)", {currentLocationRequest})
215 .withContext(std::move(positionInfoUpdateConsumerJsProxy))
216 .onThenWithContext([](const QOhosCallbackInfo &cbInfo, auto &positionInfoUpdateConsumerJsProxy) {
217 auto location = cbInfo.getFirstArg<QNapi::Object>("getCurrentLocation");
218 positionInfoUpdateConsumerJsProxy(convertLocationObjectToPositionInfo(location));
219 })
220 .onCatchWithContext([](const QOhosCallbackInfo &cbInfo, auto &positionInfoUpdateConsumerJsProxy) {
221 QtOhos::logJsCallbackError(cbInfo, "getCurrentLocation() failed");
222 positionInfoUpdateConsumerJsProxy(std::nullopt);
223 });
224 });
225
226 return sharedContext;
227}
228
230 QObject *contextObject, QOhosConsumer<LocationError> errorConsumer)
231{
232 qOhosDebug(QtForOhos) << Q_FUNC_INFO << contextObject;
233
234 auto sharedErrorConsumer = QtOhos::moveToSharedPtr(std::move(errorConsumer));
235
236 auto contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject);
237 auto weakErrorConsumer = QtOhos::makeWeakPtr(sharedErrorConsumer);
238
239 auto registrationHandle = QOhosJsThreadGateway::eval(
240 [&](QOhosJsState &jsState) {
241 return QtOhos::makeProxyWithJsThreadDeleter(
242 registerQOhosOnOffMethodsBasedEventHandler(
243 getGeoLocationManagerObject(jsState), "locationError",
244 [contextObjectRef, weakErrorConsumer](const QOhosCallbackInfo &cbInfo) {
245 auto jsLocationError = cbInfo.getFirstArg<QNapi::Number>(Q_FUNC_INFO);
246 auto locationError = cbInfo.jsState().mapOhosEnumFromJs<LocationError>(jsLocationError);
247 contextObjectRef.visitInQtThreadIfAlive(
248 [weakErrorConsumer, locationError](auto &) {
249 auto sharedErrorConsumer = weakErrorConsumer.lock();
250 if (sharedErrorConsumer)
251 (*sharedErrorConsumer)(locationError);
252 });
253 }));
254 });
255
256 return QtOhos::makeSharedPtrWithAttachedExtraData(registrationHandle, sharedErrorConsumer);
257}
258
260{
261 switch (error) {
262 case LocationError::LOCATING_FAILED_DEFAULT:
263 return QGeoPositionInfoSource::UnknownSourceError;
264 case LocationError::LOCATING_FAILED_LOCATION_SWITCH_OFF:
265 return QGeoPositionInfoSource::ClosedError;
266 case LocationError::LOCATING_FAILED_LOCATION_PERMISSION_DENIED:
267 case LocationError::LOCATING_FAILED_BACKGROUND_PERMISSION_DENIED:
268 case LocationError::LOCATING_FAILED_INTERNET_ACCESS_FAILURE:
269 return QGeoPositionInfoSource::AccessError;
270 }
271
272 qOhosPrintfWarning("%s received unknown error: %d", Q_FUNC_INFO, static_cast<int>(error));
273 return QGeoPositionInfoSource::UnknownSourceError;
274}
275
277{
278public:
280
282
284 QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const override;
286 void setPreferredPositioningMethods(PositioningMethods methods) override;
288 Error error() const override;
289
290public:
293
294 void requestUpdate(int timeout) override;
295
296protected:
298
299private:
300 void setErrorHelper(QGeoPositionInfoSource::Error error);
301 void continuousUpdateTimeout();
302
303 void updatePositionInfo(const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods);
304
306 QGeoPositionInfo m_lastUpdatedPositionInfo;
307 QGeoPositionInfo m_lastUpdatedPositionInfoFromSatelliteSource;
308
309 std::shared_ptr<void> m_locationErrorHandle;
310
311 std::shared_ptr<void> m_continuousLocationUpdatesHandle;
312 QTimer m_continuousUpdateTimeoutTimer;
313 qint64 m_lastUpdateTime = 0;
314 bool m_continuousUpdateTimeoutErrorRaised = false;
315
316 std::shared_ptr<void> m_singlePositionUpdateHandle;
317 std::unique_ptr<QTimer> m_singleUpdateTimeoutTimer;
318};
319
321{
323 qOhosDebug(QtForOhos)
324 << Q_FUNC_INFO
325 << "Location permission isn't granted. Can't create QOhosGeoPositionInfoSource";
326 return nullptr;
327 }
328
329 return new QOhosGeoPositionInfoSource(parent);
330}
331
334{
335 qOhosDebug(QtForOhos) << Q_FUNC_INFO << parent;
336
337 m_locationErrorHandle = registerLocationErrorHandler(
338 this,
339 [this](LocationError error) {
340 qOhosWarning(QtForOhos)
341 << Q_FUNC_INFO << "Received error from callback:" << toStaticString(error);
342 setErrorHelper(convertLocationErrorToQtError(error));
343 });
344
345 m_continuousUpdateTimeoutTimer.setSingleShot(false);
346 m_continuousUpdateTimeoutTimer.setInterval(continuousUpdateTimerIntervalMs);
347 QObject::connect(&m_continuousUpdateTimeoutTimer, &QTimer::timeout,
348 this, [this]() { continuousUpdateTimeout(); });
349}
350
352
354{
355 qOhosDebug(QtForOhos) << Q_FUNC_INFO << msec;
356
357 QGeoPositionInfoSource::setUpdateInterval(msec);
358
359 if (m_continuousLocationUpdatesHandle) {
362 }
363}
364
365QGeoPositionInfo QOhosGeoPositionInfoSource::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
366{
367 qOhosDebug(QtForOhos) << Q_FUNC_INFO << fromSatellitePositioningMethodsOnly;
368
369 return fromSatellitePositioningMethodsOnly
370 ? m_lastUpdatedPositionInfoFromSatelliteSource
371 : m_lastUpdatedPositionInfo;
372}
373
375{
376 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
377
378 return QGeoPositionInfoSource::AllPositioningMethods;
379}
380
382{
383 qOhosDebug(QtForOhos) << Q_FUNC_INFO << methods;
384
385 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
386}
387
389{
390 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
391
392 return 1000;
393}
394
396{
397 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
398
399 return m_error;
400}
401
403{
404 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
405
406 if (m_continuousLocationUpdatesHandle) {
407 qOhosDebug(QtForOhos) << Q_FUNC_INFO << "Continuous updates already running. Ignoring";
408 return;
409 }
410
411 auto intervalMs = std::max(updateInterval(), minimumUpdateInterval());
412
413 m_lastUpdateTime = QDateTime::currentMSecsSinceEpoch();
414 m_continuousUpdateTimeoutErrorRaised = false;
415
416 m_continuousLocationUpdatesHandle =
417 makeContinuousPositionInfoUpdateProducer(
418 this, intervalMs,
419 preferredPositioningMethods(),
420 [this](const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods) {
421 updatePositionInfo(positionInfo, positioningMethods);
422 });
423
424 m_continuousUpdateTimeoutTimer.start();
425
426 setErrorHelper(QGeoPositionInfoSource::NoError);
427}
428
429void QOhosGeoPositionInfoSource::continuousUpdateTimeout()
430{
431 if (!m_continuousUpdateTimeoutErrorRaised) {
432 const auto now = QDateTime::currentMSecsSinceEpoch();
433 if ((now - m_lastUpdateTime) > (updateInterval() + coldStartMarginMs)) {
434 m_continuousUpdateTimeoutErrorRaised = true;
435 setErrorHelper(QGeoPositionInfoSource::UpdateTimeoutError);
436 }
437 }
438}
439
441{
442 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
443
444 m_continuousLocationUpdatesHandle.reset();
445 m_continuousUpdateTimeoutTimer.stop();
446}
447
449{
450 qOhosDebug(QtForOhos) << Q_FUNC_INFO << timeoutMs;
451
452 if (m_singlePositionUpdateHandle) {
453 qOhosDebug(QtForOhos) << Q_FUNC_INFO << "Update already in progress. Ignoring this one";
454 return;
455 }
456
457 if (timeoutMs != 0 && timeoutMs < minimumUpdateInterval()) {
458 setErrorHelper(QGeoPositionInfoSource::UpdateTimeoutError);
459 return;
460 }
461
462 auto timeout = timeoutMs != 0 ? timeoutMs : coldStartMarginMs;
463
464 m_singlePositionUpdateHandle = makeSinglePositionInfoUpdateProducer(
465 this,
466 updateInterval() >= minimumUpdateInterval() ? updateInterval() : minimumUpdateInterval(),
467 preferredPositioningMethods(),
468 [this](std::optional<QGeoPositionInfo> optPositionInfo, PositioningMethods positioningMethods) {
469 m_singlePositionUpdateHandle.reset();
470 m_singleUpdateTimeoutTimer.reset();
471 if (optPositionInfo.has_value())
472 updatePositionInfo(optPositionInfo.value(), positioningMethods);
473 else
474 setErrorHelper(QGeoPositionInfoSource::UpdateTimeoutError);
475 });
476 if (!m_singlePositionUpdateHandle) {
477 qOhosWarning(QtForOhos) << Q_FUNC_INFO << "Failed to make single position info update producer";
478 setErrorHelper(QGeoPositionInfoSource::UnknownSourceError);
479 return;
480 }
481
482 m_singleUpdateTimeoutTimer =
483 makeSingleShotUpdateTimeoutTimer(timeout, [this]() {
484 m_singlePositionUpdateHandle.reset();
485 setErrorHelper(QGeoPositionInfoSource::UpdateTimeoutError);
486 });
487
488 setErrorHelper(QGeoPositionInfoSource::NoError);
489}
490
491void QOhosGeoPositionInfoSource::setErrorHelper(QGeoPositionInfoSource::Error newError)
492{
493 qOhosDebug(QtForOhos) << Q_FUNC_INFO << newError;
494
495 if (m_error == newError)
496 return;
497
498 m_error = newError;
499
500 if (newError != QGeoPositionInfoSource::NoError)
501 Q_EMIT QGeoPositionInfoSource::errorOccurred(m_error);
502}
503
504void QOhosGeoPositionInfoSource::updatePositionInfo(
505 const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods)
506{
507 qOhosDebug(QtForOhos) << Q_FUNC_INFO << positionInfo;
508
509 m_lastUpdateTime = QDateTime::currentMSecsSinceEpoch();
510 m_continuousUpdateTimeoutErrorRaised = false;
511 setErrorHelper(QGeoPositionInfoSource::NoError);
512
513 if (positioningMethods.testFlag(PositioningMethod::SatellitePositioningMethods))
514 m_lastUpdatedPositionInfoFromSatelliteSource = positionInfo;
515 m_lastUpdatedPositionInfo = positionInfo;
516 Q_EMIT positionUpdated(m_lastUpdatedPositionInfo);
517}
518
519}
520
522{
523 return QOhosGeoPositionInfoSource::tryMakeInstance(parent);
524}
525
526QT_END_NAMESPACE
void setPreferredPositioningMethods(PositioningMethods methods) override
void startUpdates() override
Starts emitting updates at regular intervals as specified by setUpdateInterval().
PositioningMethods supportedPositioningMethods() const override
Returns the positioning methods available to this source.
void requestUpdate(int timeout) override
Attempts to get the current position and emit positionUpdated() with this information.
static QOhosGeoPositionInfoSource * tryMakeInstance(QObject *parent)
Error error() const override
Returns the type of error that last occurred.
QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const override
Returns an update containing the last known position, or a null update if none is available.
void stopUpdates() override
Stops emitting updates at regular intervals.
std::shared_ptr< void > registerLocationChangeUpdateHandler(QObject *contextObject, long intervalSec, UserActivityScenario userActivityScenario, QOhosConsumer< const QGeoPositionInfo & > positionInfoUpdateConsumer)
std::shared_ptr< void > registerLocationErrorHandler(QObject *contextObject, QOhosConsumer< LocationError > errorConsumer)
std::optional< UserActivityScenario > tryMapPositioningMethodsToUserActivityScenario(QGeoPositionInfoSource::PositioningMethods positioningMethods)
const char * toStaticString(UserActivityScenario scenario)
QGeoPositionInfo convertLocationObjectToPositionInfo(const QNapi::Object &locationObject)
QGeoPositionInfoSource::Error convertLocationErrorToQtError(LocationError error)
constexpr int continuousUpdateTimerIntervalMs
QtOhos::enums::ohos::geoLocationManager::LocationError LocationError
const char * toStaticString(LocationError locationError)
std::shared_ptr< void > makeContinuousPositionInfoUpdateProducer(QObject *contextObject, int intervalMs, QGeoPositionInfoSource::PositioningMethods positioningMethods, QOhosConsumer< const QGeoPositionInfo &, QGeoPositionInfoSource::PositioningMethods > positionInfoUpdateConsumer)
QtOhos::enums::ohos::geoLocationManager::UserActivityScenario UserActivityScenario
std::shared_ptr< void > makeSinglePositionInfoUpdateProducer(QObject *contextObject, int timeoutMs, QGeoPositionInfoSource::PositioningMethods positioningMethods, QOhosConsumer< std::optional< QGeoPositionInfo >, QGeoPositionInfoSource::PositioningMethods > positionInfoUpdateConsumer)
QGeoPositionInfoSource * tryMakeQOhosGeoPositionInfoSource(QObject *parent)
bool checkLocationOrApproximatelyLocationPermissionGranted()