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 "qohosjsutils.h"
7#include <QtCore/private/qcore_ohos_p.h>
8#include <QtCore/private/qohoscommon_p.h>
9#include <QtCore/private/qohoslogger_p.h>
10#include <QtCore/qtimer.h>
11#include <algorithm>
12#include <cmath>
13#include <cstdlib>
14#include <functional>
15#include <memory>
16#include <optional>
17#include <utility>
18
19QT_BEGIN_NAMESPACE
20
21namespace {
22
30
39
41{
42 switch (scenario) {
44 return "NAVIGATION";
46 return "SPORT";
48 return "TRANSPORT";
50 return "DAILY_LIFE_SERVICE";
51 }
52
53 qOhosReportFatalErrorAndAbort(
54 "%s received invalid UserActivityScenario: %d", Q_FUNC_INFO, scenario);
55}
56
57const char *toStaticString(LocationError locationError)
58{
59 switch (locationError) {
61 return "LOCATING_FAILED_DEFAULT";
63 return "LOCATING_FAILED_LOCATION_PERMISSION_DENIED";
65 return "LOCATING_FAILED_BACKGROUND_PERMISSION_DENIED";
67 return "LOCATING_FAILED_LOCATION_SWITCH_OFF";
69 return "LOCATING_FAILED_INTERNET_ACCESS_FAILURE";
70 }
71
72 qOhosReportFatalErrorAndAbort(
73 "%s received invalid LocationError: %d", Q_FUNC_INFO, locationError);
74}
75
77 QGeoPositionInfoSource::PositioningMethods positioningMethods)
78{
79 qOhosPrintfDebug("%s: %d", Q_FUNC_INFO, static_cast<int>(positioningMethods));
80
81 if (positioningMethods.testFlag(QGeoPositionInfoSource::SatellitePositioningMethods))
83
84 if (positioningMethods.testFlag(QGeoPositionInfoSource::NonSatellitePositioningMethods))
86
87 return std::nullopt;
88}
89
90QGeoPositionInfo convertLocationObjectToPositionInfo(const QNapi::Object &locationObject)
91{
92 auto dateTime = QDateTime::fromMSecsSinceEpoch(
93 locationObject.get<QNapi::Number>("timeStamp").Int64Value());
94 QGeoCoordinate coordinate(
95 locationObject.get<QNapi::Number>("latitude"),
96 locationObject.get<QNapi::Number>("longitude"),
97 locationObject.get<QNapi::Number>("altitude"));
98
99 QGeoPositionInfo positionInfo(coordinate, dateTime);
100
101 positionInfo.setAttribute(
102 QGeoPositionInfo::Direction, locationObject.get<QNapi::Number>("direction"));
103 positionInfo.setAttribute(
104 QGeoPositionInfo::HorizontalAccuracy, locationObject.get<QNapi::Number>("accuracy"));
105 positionInfo.setAttribute(
106 QGeoPositionInfo::VerticalAccuracy, locationObject.get<QNapi::Number>("altitudeAccuracy"));
107 // NOTE: QGeoPositionInfo distinguishes GroundSpeed and VerticalSpeed, while OHOS provides only
108 // Location_BasicInfo::speed. Here, we assume, that since Location_BasicInfo::accuracy relates
109 // to HorizontalAccuracy, the Location_BasicInfo::speed relates to GroundSpeed (although, there
110 // is no information about that anywhere in the documentation).
111 positionInfo.setAttribute(
112 QGeoPositionInfo::GroundSpeed, locationObject.get<QNapi::Number>("speed"));
113
114 return positionInfo;
115}
116
118 QObject *contextObject, long intervalSec,
119 UserActivityScenario userActivityScenario,
120 QOhosConsumer<const QGeoPositionInfo &> positionInfoUpdateConsumer)
121{
122 auto sharedPositionInfoUpdateConsumer = QtOhos::moveToSharedPtr(std::move(positionInfoUpdateConsumer));
123
124 auto weakPositionInfoUpdateConsumer = QtOhos::makeWeakPtr(sharedPositionInfoUpdateConsumer);
125 auto contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject);
126
127 auto registrationHandle = QOhosJsThreadGateway::eval(
128 [&](QOhosJsState &jsState) {
129 qOhosPrintfWarning("%s Interval in Seconds: %li", Q_FUNC_INFO, intervalSec);
130 auto continuousLocationRequest = QNapi::makeObject(
131 jsState.env(),
132 {
133 {"interval", intervalSec},
134 {"locationScenario", jsState.mapOhosEnumToJs(userActivityScenario)},
135 });
136
137 qOhosPrintfDebug(
138 "%s registering callback with scenario: %s",
139 Q_FUNC_INFO, toStaticString(userActivityScenario));
140
141 return QtOhos::registerOnOffMethodsBasedEventHandler(
142 getGeoLocationManagerObject(jsState), "locationChange",
143 [contextObjectRef, weakPositionInfoUpdateConsumer](const QOhosCallbackInfo &cbInfo) {
144 auto location = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
145 auto positionInfo = convertLocationObjectToPositionInfo(location);
146
147 contextObjectRef.visitInQtThreadIfAlive(
148 [weakPositionInfoUpdateConsumer, positionInfo](auto &) {
149 auto sharedPositionInfoUpdateConsumer = weakPositionInfoUpdateConsumer.lock();
150 if (sharedPositionInfoUpdateConsumer)
151 (*sharedPositionInfoUpdateConsumer)(positionInfo);
152 });
153 },
154 {
155 .extraOnArg = std::make_optional<QNapi::ValueWrapper>(continuousLocationRequest),
156 });
157 });
158
159 return QtOhos::moveToSharedPtr(
160 std::make_pair(sharedPositionInfoUpdateConsumer, registrationHandle));
161}
162
164 QObject *contextObject, int intervalMs, QGeoPositionInfoSource::PositioningMethods positioningMethods,
165 QOhosConsumer<const QGeoPositionInfo &, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer)
166{
167 auto userActivityScenario =
168 tryMapPositioningMethodsToUserActivityScenario(positioningMethods)
169 .value_or(UserActivityScenario::DAILY_LIFE_SERVICE);
170 auto intervalSec = std::lround(static_cast<double>(intervalMs) / 1000.0);
171
172 return registerLocationChangeUpdateHandler(
173 contextObject, intervalSec, userActivityScenario,
174 [positionInfoUpdateConsumer = std::move(positionInfoUpdateConsumer), positioningMethods](const QGeoPositionInfo &positionInfo) {
175 positionInfoUpdateConsumer(positionInfo, positioningMethods);
176 });
177}
178
180 QObject *contextObject, int timeoutMs, QGeoPositionInfoSource::PositioningMethods positioningMethods,
181 QOhosConsumer<std::optional<QGeoPositionInfo>, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer)
182{
183 qOhosDebug(QtForOhos) << Q_FUNC_INFO << contextObject << timeoutMs;
184
185 struct Context
186 {
187 QOhosConsumer<std::optional<QGeoPositionInfo>, QGeoPositionInfoSource::PositioningMethods> positionInfoUpdateConsumer;
188 QtOhos::QThreadSafeRef<QObject> contextObjectRef;
189 };
190
191 auto sharedContext = QtOhos::moveToSharedPtr(Context{
192 .positionInfoUpdateConsumer = std::move(positionInfoUpdateConsumer),
193 .contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject)
194 });
195
196 auto weakContext = QtOhos::makeWeakPtr(sharedContext);
197 auto positionInfoUpdateConsumerJsProxy =
198 [weakContext, positioningMethods](std::optional<QGeoPositionInfo> optPositionInfo) {
199 auto sharedContext = weakContext.lock();
200 if (sharedContext) {
201 sharedContext->contextObjectRef.visitInQtThreadIfAlive(
202 [weakContext, optPositionInfo, positioningMethods](auto &) {
203 auto sharedContext = weakContext.lock();
204 if (sharedContext)
205 sharedContext->positionInfoUpdateConsumer(optPositionInfo, positioningMethods);
206 });
207 }
208 };
209
210 QOhosJsThreadGateway::runAndWait(
211 [&](QOhosJsState &jsState) {
212 auto userActivityScenario =
213 tryMapPositioningMethodsToUserActivityScenario(positioningMethods)
214 .value_or(UserActivityScenario::DAILY_LIFE_SERVICE);
215 auto currentLocationRequest = QNapi::makeObject(
216 jsState.env(),
217 {
218 {"scenario", jsState.mapOhosEnumToJs(userActivityScenario)},
219 {"timeoutMs", timeoutMs},
220 });
221
222 getGeoLocationManagerObject(jsState)
223 .evalToPromiseOrRejectOnThrow("getCurrentLocation(*)", {currentLocationRequest})
224 .withContext(std::move(positionInfoUpdateConsumerJsProxy))
225 .onThenWithContext([](const QOhosCallbackInfo &cbInfo, auto &positionInfoUpdateConsumerJsProxy) {
226 auto location = cbInfo.getFirstArg<QNapi::Object>("getCurrentLocation");
227 positionInfoUpdateConsumerJsProxy(convertLocationObjectToPositionInfo(location));
228 })
229 .onCatchWithContext([](const QOhosCallbackInfo &cbInfo, auto &positionInfoUpdateConsumerJsProxy) {
230 QtOhos::logJsCallbackError(cbInfo, "getCurrentLocation() failed");
231 positionInfoUpdateConsumerJsProxy(std::nullopt);
232 });
233 });
234
235 return sharedContext;
236}
237
239 QObject *contextObject, QOhosConsumer<LocationError> errorConsumer)
240{
241 qOhosDebug(QtForOhos) << Q_FUNC_INFO << contextObject;
242
243 auto sharedErrorConsumer = QtOhos::moveToSharedPtr(std::move(errorConsumer));
244
245 auto contextObjectRef = QtOhos::makeQThreadSafeRef(contextObject);
246 auto weakErrorConsumer = QtOhos::makeWeakPtr(sharedErrorConsumer);
247
248 auto registrationHandle = QOhosJsThreadGateway::eval(
249 [&](QOhosJsState &jsState) {
250 return QtOhos::registerOnOffMethodsBasedEventHandler(
251 getGeoLocationManagerObject(jsState), "locationError",
252 [contextObjectRef, weakErrorConsumer](const QOhosCallbackInfo &cbInfo) {
253 auto jsLocationError = cbInfo.getFirstArg<QNapi::Number>(Q_FUNC_INFO);
254 auto locationError = cbInfo.jsState().mapOhosEnumFromJs<LocationError>(jsLocationError);
255 contextObjectRef.visitInQtThreadIfAlive(
256 [weakErrorConsumer, locationError](auto &) {
257 auto sharedErrorConsumer = weakErrorConsumer.lock();
258 if (sharedErrorConsumer)
259 (*sharedErrorConsumer)(locationError);
260 });
261 });
262 });
263
264 return QtOhos::makeSharedPtrWithAttachedExtraData(registrationHandle, sharedErrorConsumer);
265}
266
268{
269 switch (error) {
270 case LocationError::LOCATING_FAILED_DEFAULT:
271 return QGeoPositionInfoSource::UnknownSourceError;
272 case LocationError::LOCATING_FAILED_LOCATION_SWITCH_OFF:
273 return QGeoPositionInfoSource::ClosedError;
274 case LocationError::LOCATING_FAILED_LOCATION_PERMISSION_DENIED:
275 case LocationError::LOCATING_FAILED_BACKGROUND_PERMISSION_DENIED:
276 case LocationError::LOCATING_FAILED_INTERNET_ACCESS_FAILURE:
277 return QGeoPositionInfoSource::AccessError;
278 }
279
280 qOhosPrintfWarning("%s received unknown error: %d", Q_FUNC_INFO, static_cast<int>(error));
281 return QGeoPositionInfoSource::UnknownSourceError;
282}
283
285{
286public:
288
290
292 QGeoPositionInfo lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const override;
294 void setPreferredPositioningMethods(PositioningMethods methods) override;
296 Error error() const override;
297
298public:
301
302 void requestUpdate(int timeout) override;
303
304protected:
306
307private:
308 void setErrorHelper(QGeoPositionInfoSource::Error error);
309
310 void updatePositionInfo(const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods);
311
313 QGeoPositionInfo m_lastUpdatedPositionInfo;
314 QGeoPositionInfo m_lastUpdatedPositionInfoFromSatelliteSource;
315
316 std::shared_ptr<void> m_locationErrorHandle;
317
318 std::shared_ptr<void> m_continuousLocationUpdatesHandle;
319 std::unique_ptr<QTimer> m_continuousUpdateTimeoutTimer;
320
321 std::shared_ptr<void> m_singlePositionUpdateHandle;
322 std::unique_ptr<QTimer> m_singleUpdateTimeoutTimer;
323};
324
326{
328 qOhosDebug(QtForOhos)
329 << Q_FUNC_INFO
330 << "Location permission isn't granted. Can't create QOhosGeoPositionInfoSource";
331 return nullptr;
332 }
333
334 return new QOhosGeoPositionInfoSource(parent);
335}
336
339{
340 qOhosDebug(QtForOhos) << Q_FUNC_INFO << parent;
341
342 m_locationErrorHandle = registerLocationErrorHandler(
343 this,
344 [this](LocationError error) {
345 qOhosWarning(QtForOhos)
346 << Q_FUNC_INFO << "Received error from callback:" << toStaticString(error);
347 setErrorHelper(convertLocationErrorToQtError(error));
348 });
349}
350
352
354{
355 qOhosDebug(QtForOhos) << Q_FUNC_INFO << msec;
356
357 QGeoPositionInfoSource::setUpdateInterval(msec);
358
359 if (m_continuousLocationUpdatesHandle)
361}
362
363QGeoPositionInfo QOhosGeoPositionInfoSource::lastKnownPosition(bool fromSatellitePositioningMethodsOnly) const
364{
365 qOhosDebug(QtForOhos) << Q_FUNC_INFO << fromSatellitePositioningMethodsOnly;
366
367 return fromSatellitePositioningMethodsOnly
368 ? m_lastUpdatedPositionInfoFromSatelliteSource
369 : m_lastUpdatedPositionInfo;
370}
371
373{
374 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
375
376 return QGeoPositionInfoSource::AllPositioningMethods;
377}
378
380{
381 qOhosDebug(QtForOhos) << Q_FUNC_INFO << methods;
382
383 QGeoPositionInfoSource::setPreferredPositioningMethods(methods);
384}
385
387{
388 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
389
390 return 1000;
391}
392
394{
395 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
396
397 return m_error;
398}
399
401{
402 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
403
404 constexpr int allowedTimeoutDelayMs = 1000;
405
406 auto intervalMs = std::max(updateInterval(), minimumUpdateInterval());
407
408 m_continuousLocationUpdatesHandle =
409 makeContinuousPositionInfoUpdateProducer(
410 this, intervalMs,
411 preferredPositioningMethods(),
412 [this, intervalMs](const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods) {
413 updatePositionInfo(positionInfo, positioningMethods);
414
415 m_continuousUpdateTimeoutTimer =
416 makeSingleShotUpdateTimeoutTimer(
417 intervalMs + allowedTimeoutDelayMs, [this]() {
418 Q_EMIT errorOccurred(QGeoPositionInfoSource::UpdateTimeoutError);
419 });
420 });
421
422 m_continuousUpdateTimeoutTimer =
423 makeSingleShotUpdateTimeoutTimer(
424 intervalMs + allowedTimeoutDelayMs, [this]() {
425 Q_EMIT errorOccurred(QGeoPositionInfoSource::UpdateTimeoutError);
426 });
427
428 setErrorHelper(QGeoPositionInfoSource::NoError);
429}
430
432{
433 qOhosDebug(QtForOhos) << Q_FUNC_INFO;
434
435 m_continuousLocationUpdatesHandle.reset();
436}
437
439{
440 qOhosDebug(QtForOhos) << Q_FUNC_INFO << timeoutMs;
441
442 constexpr int defaultTimeoutMs = 3000;
443
444 if (m_singlePositionUpdateHandle) {
445 qOhosDebug(QtForOhos) << Q_FUNC_INFO << "Update already in progress. Ignoring this one";
446 return;
447 }
448
449 if (timeoutMs != 0 && timeoutMs < minimumUpdateInterval()) {
450 Q_EMIT errorOccurred(QGeoPositionInfoSource::UpdateTimeoutError);
451 return;
452 }
453
454 auto timeout = timeoutMs != 0 ? timeoutMs : defaultTimeoutMs;
455
456 m_singlePositionUpdateHandle = makeSinglePositionInfoUpdateProducer(
457 this,
458 updateInterval() >= minimumUpdateInterval() ? updateInterval() : minimumUpdateInterval(),
459 preferredPositioningMethods(),
460 [this](std::optional<QGeoPositionInfo> optPositionInfo, PositioningMethods positioningMethods) {
461 m_singlePositionUpdateHandle.reset();
462 m_singleUpdateTimeoutTimer.reset();
463 if (optPositionInfo.has_value()) {
464 updatePositionInfo(optPositionInfo.value(), positioningMethods);
465 } else {
466 setErrorHelper(QGeoPositionInfoSource::UnknownSourceError);
467 Q_EMIT errorOccurred(QGeoPositionInfoSource::UpdateTimeoutError);
468 }
469 });
470 if (!m_singlePositionUpdateHandle) {
471 qOhosWarning(QtForOhos) << Q_FUNC_INFO << "Failed to make single position info update producer";
472 setErrorHelper(QGeoPositionInfoSource::UnknownSourceError);
473 return;
474 }
475
476 m_singleUpdateTimeoutTimer =
477 makeSingleShotUpdateTimeoutTimer(timeout, [this]() {
478 m_singlePositionUpdateHandle.reset();
479 Q_EMIT errorOccurred(QGeoPositionInfoSource::UpdateTimeoutError);
480 });
481
482 setErrorHelper(QGeoPositionInfoSource::NoError);
483}
484
485void QOhosGeoPositionInfoSource::setErrorHelper(QGeoPositionInfoSource::Error newError)
486{
487 qOhosDebug(QtForOhos) << Q_FUNC_INFO << newError;
488
489 if (m_error == newError)
490 return;
491
492 m_error = newError;
493
494 if (newError != QGeoPositionInfoSource::NoError)
495 Q_EMIT QGeoPositionInfoSource::errorOccurred(m_error);
496}
497
498void QOhosGeoPositionInfoSource::updatePositionInfo(
499 const QGeoPositionInfo &positionInfo, PositioningMethods positioningMethods)
500{
501 qOhosDebug(QtForOhos) << Q_FUNC_INFO << positionInfo;
502
503 if (positioningMethods.testFlag(PositioningMethod::SatellitePositioningMethods))
504 m_lastUpdatedPositionInfoFromSatelliteSource = positionInfo;
505 m_lastUpdatedPositionInfo = positionInfo;
506 Q_EMIT positionUpdated(m_lastUpdatedPositionInfo);
507}
508
509}
510
512{
513 return QOhosGeoPositionInfoSource::tryMakeInstance(parent);
514}
515
516namespace QtOhos {
517
518template<>
520{
521 static constexpr const char *fullTypeName = "@ohos.geoLocationManager.UserActivityScenario";
522 static constexpr std::array<std::pair<UserActivityScenario, const char *>, 4> enumeratorsNames = {{
523 {UserActivityScenario::NAVIGATION, "NAVIGATION"},
525 {UserActivityScenario::TRANSPORT, "TRANSPORT"},
526 {UserActivityScenario::DAILY_LIFE_SERVICE, "DAILY_LIFE_SERVICE"},
527 }};
528};
529
530template<>
532{
533 static constexpr const char *fullTypeName = "@ohos.geoLocationManager.LocationError";
534 static constexpr std::array<std::pair<LocationError, const char *>, 5> enumeratorsNames = {{
535 {LocationError::LOCATING_FAILED_DEFAULT, "LOCATING_FAILED_DEFAULT"},
536 {LocationError::LOCATING_FAILED_LOCATION_PERMISSION_DENIED, "LOCATING_FAILED_LOCATION_PERMISSION_DENIED"},
537 {LocationError::LOCATING_FAILED_BACKGROUND_PERMISSION_DENIED, "LOCATING_FAILED_BACKGROUND_PERMISSION_DENIED"},
538 {LocationError::LOCATING_FAILED_LOCATION_SWITCH_OFF, "LOCATING_FAILED_LOCATION_SWITCH_OFF"},
539 {LocationError::LOCATING_FAILED_INTERNET_ACCESS_FAILURE, "LOCATING_FAILED_INTERNET_ACCESS_FAILURE"},
540 }};
541};
542
543}
544
545QT_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)
const char * toStaticString(LocationError locationError)
std::shared_ptr< void > makeContinuousPositionInfoUpdateProducer(QObject *contextObject, int intervalMs, QGeoPositionInfoSource::PositioningMethods positioningMethods, QOhosConsumer< const QGeoPositionInfo &, QGeoPositionInfoSource::PositioningMethods > positionInfoUpdateConsumer)
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()
static constexpr std::array< std::pair< UserActivityScenario, const char * >, 4 > enumeratorsNames