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
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 .optEventSourceAliveCheckFunc = {},
147 .extraOnArg = std::make_optional<QNapi::ValueWrapper>(continuousLocationRequest),
148 .extraOffArg = {},
149 .optOnCallExceptionHandler = {},
150 }));
151 });
152
153 return QtOhos::moveToSharedPtr(
154 std::make_pair(sharedPositionInfoUpdateConsumer, registrationHandle));
155}
156
158 QObject *contextObject, int intervalMs, QGeoPositionInfoSource::PositioningMethods positioningMethods,
159 QOhosConsumer<const QGeoPositionInfo &, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer)
160{
161 auto userActivityScenario =
162 tryMapPositioningMethodsToUserActivityScenario(positioningMethods)
163 .value_or(UserActivityScenario::DAILY_LIFE_SERVICE);
164 auto intervalSec = std::lround(static_cast<double>(intervalMs) / 1000.0);
165
166 return registerLocationChangeUpdateHandler(
167 contextObject, intervalSec, userActivityScenario,
168 [positionInfoUpdateConsumer = std::move(positionInfoUpdateConsumer), positioningMethods](const QGeoPositionInfo &positionInfo) {
169 positionInfoUpdateConsumer(positionInfo, positioningMethods);
170 });
171}
172
174 QObject *contextObject, int timeoutMs, QGeoPositionInfoSource::PositioningMethods positioningMethods,
175 QOhosConsumer<std::optional<QGeoPositionInfo>, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer)
176{
177 qOhosDebug(QtForOhos) << Q_FUNC_INFO << contextObject << timeoutMs;
178
179 struct Context
180 {
181 QOhosConsumer<std::optional<QGeoPositionInfo>, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer;
182 QtOhos::QThreadSafeRef<QObject> contextObjectRef;
183 };
184
185 auto sharedContext = QtOhos::moveToSharedPtr(Context{
186 .positionInfoUpdateConsumer = std::move(positionInfoUpdateConsumer),
187 .contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject)
188 });
189
190 auto weakContext = QtOhos::makeWeakPtr(sharedContext);
191 auto sharedPositionInfoUpdateProxy = QtOhos::moveToSharedPtr(
192 [weakContext, positioningMethods](std::optional<QGeoPositionInfo> optPositionInfo) {
193 auto sharedContext = weakContext.lock();
194 if (sharedContext) {
195 sharedContext->contextObjectRef.visitInQtThreadIfAlive(
196 [weakContext, optPositionInfo, positioningMethods](auto &) {
197 auto sharedContext = weakContext.lock();
198 if (sharedContext)
199 sharedContext->positionInfoUpdateConsumer(optPositionInfo, positioningMethods);
200 });
201 }
202 });
203
204 QOhosJsThreadGateway::runAndWait(
205 [&](QOhosJsState &jsState) {
206 auto userActivityScenario =
207 tryMapPositioningMethodsToUserActivityScenario(positioningMethods)
208 .value_or(UserActivityScenario::DAILY_LIFE_SERVICE);
209 auto currentLocationRequest = QNapi::makeObject(
210 jsState.env(),
211 {
212 {"scenario", jsState.mapOhosEnumToJs(userActivityScenario)},
213 {"timeoutMs", timeoutMs},
214 });
215
216 getGeoLocationManagerObject(jsState)
217 .evalToPromiseOrRejectOnThrow("getCurrentLocation(*)", {currentLocationRequest})
218 .onThen([sharedPositionInfoUpdateProxy](const QOhosCallbackInfo &cbInfo) {
219 auto location = cbInfo.getFirstArg<QNapi::Object>("getCurrentLocation");
220 (*sharedPositionInfoUpdateProxy)(convertLocationObjectToPositionInfo(location));
221 })
222 .onCatch([sharedPositionInfoUpdateProxy](const QOhosCallbackInfo &cbInfo) {
223 QtOhos::logJsCallbackError(cbInfo, "getCurrentLocation() failed");
224 (*sharedPositionInfoUpdateProxy)(std::nullopt);
225 });
226 });
227
228 return sharedContext;
229}
230
232 QObject *contextObject, QOhosConsumer<LocationError> errorConsumer)
233{
234 qOhosDebug(QtForOhos) << Q_FUNC_INFO << contextObject;
235
236 auto sharedErrorConsumer = QtOhos::moveToSharedPtr(std::move(errorConsumer));
237
238 auto contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject);
239 auto weakErrorConsumer = QtOhos::makeWeakPtr(sharedErrorConsumer);
240
241 auto registrationHandle = QOhosJsThreadGateway::eval(
242 [&](QOhosJsState &jsState) {
243 return QtOhos::makeProxyWithJsThreadDeleter(
244 registerQOhosOnOffMethodsBasedEventHandler(
245 getGeoLocationManagerObject(jsState), "locationError",
246 [contextObjectRef, weakErrorConsumer](const QOhosCallbackInfo &cbInfo) {
247 auto jsLocationError = cbInfo.getFirstArg<QNapi::Number>(Q_FUNC_INFO);
248 auto locationError = cbInfo.jsState().mapOhosEnumFromJs<LocationError>(jsLocationError);
249 contextObjectRef.visitInQtThreadIfAlive(
250 [weakErrorConsumer, locationError](auto &) {
251 auto sharedErrorConsumer = weakErrorConsumer.lock();
252 if (sharedErrorConsumer)
253 (*sharedErrorConsumer)(locationError);
254 });
255 }));
256 });
257
258 return QtOhos::makeSharedPtrWithAttachedExtraData(registrationHandle, sharedErrorConsumer);
259}
260
262{
263 switch (error) {
264 case LocationError::LOCATING_FAILED_DEFAULT:
265 return QGeoPositionInfoSource::UnknownSourceError;
266 case LocationError::LOCATING_FAILED_LOCATION_SWITCH_OFF:
267 return QGeoPositionInfoSource::ClosedError;
268 case LocationError::LOCATING_FAILED_LOCATION_PERMISSION_DENIED:
269 case LocationError::LOCATING_FAILED_BACKGROUND_PERMISSION_DENIED:
270 case LocationError::LOCATING_FAILED_INTERNET_ACCESS_FAILURE:
271 return QGeoPositionInfoSource::AccessError;
272 }
273
274 qOhosPrintfWarning("%s received unknown error: %d", Q_FUNC_INFO, static_cast<int>(error));
275 return QGeoPositionInfoSource::UnknownSourceError;
276}
277
279{
280public:
282
284
286 QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const override;
288 void setPreferredPositioningMethods(PositioningMethods methods) override;
290 Error error() const override;
291
292public:
295
296 void requestUpdate(int timeout) override;
297
298protected:
300
301private:
302 void setErrorHelper(QGeoPositionInfoSource::Error error);
303 void continuousUpdateTimeout();
304
305 void updatePositionInfo(const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods);
306
308 QGeoPositionInfo m_lastUpdatedPositionInfo;
309 QGeoPositionInfo m_lastUpdatedPositionInfoFromSatelliteSource;
310
311 std::shared_ptr<void> m_locationErrorHandle;
312
313 std::shared_ptr<void> m_continuousLocationUpdatesHandle;
314 QTimer m_continuousUpdateTimeoutTimer;
315 qint64 m_lastUpdateTime = 0;
316 bool m_continuousUpdateTimeoutErrorRaised = false;
317
318 std::shared_ptr<void> m_singlePositionUpdateHandle;
319 std::unique_ptr<QTimer> m_singleUpdateTimeoutTimer;
320};
321
323{
325 qOhosDebug(QtForOhos)
326 << Q_FUNC_INFO
327 << "Location permission isn't granted. Can't create QOhosGeoPositionInfoSource";
328 return nullptr;
329 }
330
331 return new QOhosGeoPositionInfoSource(parent);
332}
333
336{
337 qOhosDebug(QtForOhos) << Q_FUNC_INFO << parent;
338
339 m_locationErrorHandle = registerLocationErrorHandler(
340 this,
341 [this](LocationError error) {
342 qOhosWarning(QtForOhos)
343 << Q_FUNC_INFO << "Received error from callback:" << toStaticString(error);
344 setErrorHelper(convertLocationErrorToQtError(error));
345 });
346
347 m_continuousUpdateTimeoutTimer.setSingleShot(false);
348 m_continuousUpdateTimeoutTimer.setInterval(continuousUpdateTimerIntervalMs);
349 QObject::connect(&m_continuousUpdateTimeoutTimer, &QTimer::timeout,
350 this, [this]() { continuousUpdateTimeout(); });
351}
352
354
356{
357 qOhosDebug(QtForOhos) << Q_FUNC_INFO << msec;
358
359 QGeoPositionInfoSource::setUpdateInterval(msec);
360
361 if (m_continuousLocationUpdatesHandle) {
364 }
365}
366
367QGeoPositionInfo QOhosGeoPositionInfoSource::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
368{
369 qOhosDebug(QtForOhos) << Q_FUNC_INFO << fromSatellitePositioningMethodsOnly;
370
371 return fromSatellitePositioningMethodsOnly
372 ? m_lastUpdatedPositionInfoFromSatelliteSource
373 : m_lastUpdatedPositionInfo;
374}
375
377{
378 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
379
380 return QGeoPositionInfoSource::AllPositioningMethods;
381}
382
384{
385 qOhosDebug(QtForOhos) << Q_FUNC_INFO << methods;
386
387 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
388}
389
391{
392 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
393
394 return 1000;
395}
396
398{
399 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
400
401 return m_error;
402}
403
405{
406 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
407
408 if (m_continuousLocationUpdatesHandle) {
409 qOhosDebug(QtForOhos) << Q_FUNC_INFO << "Continuous updates already running. Ignoring";
410 return;
411 }
412
413 auto intervalMs = std::max(updateInterval(), minimumUpdateInterval());
414
415 m_lastUpdateTime = QDateTime::currentMSecsSinceEpoch();
416 m_continuousUpdateTimeoutErrorRaised = false;
417
418 m_continuousLocationUpdatesHandle =
419 makeContinuousPositionInfoUpdateProducer(
420 this, intervalMs,
421 preferredPositioningMethods(),
422 [this](const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods) {
423 updatePositionInfo(positionInfo, positioningMethods);
424 });
425
426 m_continuousUpdateTimeoutTimer.start();
427
428 setErrorHelper(QGeoPositionInfoSource::NoError);
429}
430
431void QOhosGeoPositionInfoSource::continuousUpdateTimeout()
432{
433 if (!m_continuousUpdateTimeoutErrorRaised) {
434 const auto now = QDateTime::currentMSecsSinceEpoch();
435 if ((now - m_lastUpdateTime) > (updateInterval() + coldStartMarginMs)) {
436 m_continuousUpdateTimeoutErrorRaised = true;
437 setErrorHelper(QGeoPositionInfoSource::UpdateTimeoutError);
438 }
439 }
440}
441
443{
444 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
445
446 m_continuousLocationUpdatesHandle.reset();
447 m_continuousUpdateTimeoutTimer.stop();
448}
449
451{
452 qOhosDebug(QtForOhos) << Q_FUNC_INFO << timeoutMs;
453
454 if (m_singlePositionUpdateHandle) {
455 qOhosDebug(QtForOhos) << Q_FUNC_INFO << "Update already in progress. Ignoring this one";
456 return;
457 }
458
459 if (timeoutMs != 0 && timeoutMs < minimumUpdateInterval()) {
460 setErrorHelper(QGeoPositionInfoSource::UpdateTimeoutError);
461 return;
462 }
463
464 auto timeout = timeoutMs != 0 ? timeoutMs : coldStartMarginMs;
465
466 m_singlePositionUpdateHandle = makeSinglePositionInfoUpdateProducer(
467 this,
468 updateInterval() >= minimumUpdateInterval() ? updateInterval() : minimumUpdateInterval(),
469 preferredPositioningMethods(),
470 [this](std::optional<QGeoPositionInfo> optPositionInfo, PositioningMethods positioningMethods) {
471 m_singlePositionUpdateHandle.reset();
472 m_singleUpdateTimeoutTimer.reset();
473 if (optPositionInfo.has_value())
474 updatePositionInfo(optPositionInfo.value(), positioningMethods);
475 else
476 setErrorHelper(QGeoPositionInfoSource::UpdateTimeoutError);
477 });
478 if (!m_singlePositionUpdateHandle) {
479 qOhosWarning(QtForOhos) << Q_FUNC_INFO << "Failed to make single position info update producer";
480 setErrorHelper(QGeoPositionInfoSource::UnknownSourceError);
481 return;
482 }
483
484 m_singleUpdateTimeoutTimer =
485 makeSingleShotUpdateTimeoutTimer(timeout, [this]() {
486 m_singlePositionUpdateHandle.reset();
487 setErrorHelper(QGeoPositionInfoSource::UpdateTimeoutError);
488 });
489
490 setErrorHelper(QGeoPositionInfoSource::NoError);
491}
492
493void QOhosGeoPositionInfoSource::setErrorHelper(QGeoPositionInfoSource::Error newError)
494{
495 qOhosDebug(QtForOhos) << Q_FUNC_INFO << newError;
496
497 if (m_error == newError)
498 return;
499
500 m_error = newError;
501
502 if (newError != QGeoPositionInfoSource::NoError)
503 Q_EMIT QGeoPositionInfoSource::errorOccurred(m_error);
504}
505
506void QOhosGeoPositionInfoSource::updatePositionInfo(
507 const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods)
508{
509 qOhosDebug(QtForOhos) << Q_FUNC_INFO << positionInfo;
510
511 m_lastUpdateTime = QDateTime::currentMSecsSinceEpoch();
512 m_continuousUpdateTimeoutErrorRaised = false;
513 setErrorHelper(QGeoPositionInfoSource::NoError);
514
515 if (positioningMethods.testFlag(PositioningMethod::SatellitePositioningMethods))
516 m_lastUpdatedPositionInfoFromSatelliteSource = positionInfo;
517 m_lastUpdatedPositionInfo = positionInfo;
518 Q_EMIT positionUpdated(m_lastUpdatedPositionInfo);
519}
520
521}
522
524{
525 return QOhosGeoPositionInfoSource::tryMakeInstance(parent);
526}
527
528QT_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
QtOhosPositioning::enums::ohos::geoLocationManager::UserActivityScenario UserActivityScenario
const char * toStaticString(LocationError locationError)
std::shared_ptr< void > makeContinuousPositionInfoUpdateProducer(QObject *contextObject, int intervalMs, QGeoPositionInfoSource::PositioningMethods positioningMethods, QOhosConsumer< const QGeoPositionInfo &, QGeoPositionInfoSource::PositioningMethods > positionInfoUpdateConsumer)
QtOhosPositioning::enums::ohos::geoLocationManager::LocationError LocationError
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()