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
qohoswantinfo.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
6#include <QtHarmonyExtras/private/qohosenums_p.h>
7#include <QtHarmonyExtras/private/qohosjsenv_p.h>
8
9#include <QtCore/qbytearray.h>
10#include <QtCore/qjsonobject.h>
11#include <QtCore/qlist.h>
12#include <QtCore/qstring.h>
13#include <QtCore/qvariant.h>
14#include <QtCore/private/qcore_ohos_p.h>
15#include <QtCore/private/qnapi_p.h>
16#include <QtCore/private/qohoscommon_p.h>
17#include <QtCore/private/qohoslogger_p.h>
18
19#include <cstddef>
20#include <cstdint>
21#include <memory>
22#include <optional>
23#include <string>
24#include <vector>
25
26#include <database/udmf/udmf_meta.h>
27#include <database/udmf/utd.h>
28
29QT_BEGIN_NAMESPACE
30
31namespace QtHarmonyExtras::Private {
32
33detail::WantInfoPriv::WantInfoPriv() = default;
34detail::WantInfoPriv::~WantInfoPriv() = default;
35
36namespace {
37
38template<typename T>
39std::optional<T> getOptionalProperty(const QNapi::Object &object, const std::string &propName)
40{
41 auto optPropValue = QNapi::getOptionalPropOrEmpty<T>(object, propName);
42 return !optPropValue.IsEmpty()
43 ? std::make_optional(optPropValue)
44 : std::nullopt;
45}
46
47std::optional<std::string> tryMapUtdTypeIdToMimeType(const std::string &utdTypeId)
48{
49 std::shared_ptr<::OH_Utd> utd(
50 ::OH_Utd_Create(utdTypeId.c_str()),
51 [](::OH_Utd *utd) {
52 if (utd != nullptr)
53 ::OH_Utd_Destroy(utd);
54 });
55
56 std::vector<std::string> mimeTypes;
57 if (utd) {
58 unsigned int typesCount = 0;
59 const char **rawMimeTypes = ::OH_Utd_GetMimeTypes(utd.get(), &typesCount);
60 if (rawMimeTypes == nullptr && typesCount != 0) {
61 qOhosReportFatalErrorAndAbort(
62 "%s: got inconsistent result from OH_Utd_GetMimeTypes() call: array is null, size is %u",
63 Q_FUNC_INFO, typesCount);
64 }
65 if (rawMimeTypes != nullptr)
66 mimeTypes = std::vector<std::string>(rawMimeTypes, rawMimeTypes + typesCount);
67 }
68
69 return !mimeTypes.empty()
70 ? std::make_optional(mimeTypes.front())
71 : std::nullopt;
72}
73
74std::optional<detail::SharedRecord> tryConvertNapiObjectToSharedRecord(QNapi::Object record)
75{
76 auto tryGetOptionalStringProp = [](const QNapi::Object &object, const std::string &propName) -> std::optional<QString> {
77 auto optProp = getOptionalProperty<QNapi::String>(object, propName);
78 return optProp.has_value()
79 ? std::make_optional(QString::fromStdString(optProp.value()))
80 : std::nullopt;
81 };
82
83 auto tryGetOptionalByteArrayProp = [](const QNapi::Object &object, const std::string &propName) -> std::optional<QByteArray> {
84 auto optProp = getOptionalProperty<QNapi::TypedArrayOf<std::uint8_t>>(object, propName);
85 return optProp.has_value()
86 ? std::make_optional(QByteArray(
87 reinterpret_cast<const char *>(optProp.value().Data()),
88 optProp.value().ByteLength()))
89 : std::nullopt;
90 };
91
92 auto tryGetOptionalJsonObjectProp = [](const QNapi::Object &object, const std::string &propName) -> std::optional<QJsonObject> {
93 auto optProp = getOptionalProperty<QNapi::Object>(object, propName);
94 return optProp.has_value()
95 ? std::make_optional(QOhosJsEnv::fromNapiValue<QJsonObject>(optProp.value()))
96 : std::nullopt;
97 };
98
99 std::string utd = record.get<QNapi::String>("utd");
100 auto optMimeType = utd != UDMF_META_HYPERLINK
101 ? tryMapUtdTypeIdToMimeType(utd)
102 : std::optional<std::string>("text/uri-list");
103 if (!optMimeType.has_value()) {
104 qOhosPrintfWarning(
105 "%s: can't map utd '%s' to mimetype, not mapping the record",
106 Q_FUNC_INFO, utd.c_str());
107 return std::nullopt;
108 }
109
110 auto content = tryGetOptionalStringProp(record, "content");
111 auto uri = tryGetOptionalStringProp(record, "uri");
112 if (!content.has_value() && !uri.has_value()) {
113 qOhosPrintfWarning(
114 "%s: cannot create Shared Record, content and uri properties are empty", Q_FUNC_INFO);
115 return std::nullopt;
116 }
117
118 auto optExtraDataJson = tryGetOptionalJsonObjectProp(record, "extraData");
119
120 return std::make_optional(
121 detail::SharedRecord{
122 .mimeType = QString::fromStdString(optMimeType.value()),
123 .content = content,
124 .filePath = uri,
125 .title = tryGetOptionalStringProp(record, "title"),
126 .label = tryGetOptionalStringProp(record, "label"),
127 .description = tryGetOptionalStringProp(record, "description"),
128 .thumbnail = tryGetOptionalByteArrayProp(record, "thumbnail"),
129 .thumbnailFilePath = tryGetOptionalStringProp(record, "thumbnailUri"),
130 .extraData = optExtraDataJson.has_value()
131 ? std::make_optional(optExtraDataJson.value().toVariantMap())
132 : std::nullopt,
133 });
134}
135
136std::optional<detail::WantInfoPriv::LaunchReason> tryMapOhosLaunchReasonToWantInfoEnum(
137 QtOhos::enums::ohos::app::ability::AbilityConstant::LaunchReason ohosLaunchReason)
138{
139 using OhosLaunchReason = QtOhos::enums::ohos::app::ability::AbilityConstant::LaunchReason;
140 using WantInfoPriv = detail::WantInfoPriv;
141
142 switch (ohosLaunchReason) {
143 case OhosLaunchReason::START_ABILITY:
144 return std::make_optional(WantInfoPriv::LaunchReason::START_ABILITY);
145 case OhosLaunchReason::CONTINUATION:
146 return std::make_optional(WantInfoPriv::LaunchReason::CONTINUATION);
147 case OhosLaunchReason::PREPARE_CONTINUATION:
148 return std::make_optional(WantInfoPriv::LaunchReason::PREPARE_CONTINUATION);
149 case OhosLaunchReason::PRELOAD:
150 return std::make_optional(WantInfoPriv::LaunchReason::PRELOAD);
151 case OhosLaunchReason::UNKNOWN:
152 case OhosLaunchReason::CALL:
153 case OhosLaunchReason::APP_RECOVERY:
154 case OhosLaunchReason::SHARE:
155 case OhosLaunchReason::AUTO_STARTUP:
156 case OhosLaunchReason::INSIGHT_INTENT:
157 return std::make_optional(WantInfoPriv::LaunchReason::UNKNOWN);
158 }
159
160 return {};
161}
162
163detail::WantInfoPriv::LaunchReason mapJsLaunchReasonToWantInfoEnumWithFallback(
164 QOhosJsState &jsState, QNapi::Number jsLaunchReason)
165{
166 auto optLaunchReasonJsEnum =
167 jsState.tryMapOhosEnumFromJs<QtOhos::enums::ohos::app::ability::AbilityConstant::LaunchReason>(jsLaunchReason);
168 auto optLaunchReason =
169 optLaunchReasonJsEnum.has_value()
170 ? tryMapOhosLaunchReasonToWantInfoEnum(optLaunchReasonJsEnum.value())
171 : std::nullopt;
172 return optLaunchReason.value_or(detail::WantInfoPriv::LaunchReason::UNKNOWN);
173}
174
175class WantInfoImpl : public detail::WantInfoPriv
176{
177public:
178 WantInfoImpl(QNapi::Object want, LaunchReason launchReason);
179
180 QJsonObject jsonObject() const override;
181
182 std::optional<QList<detail::SharedRecord>> tryGetSharedDataRecords() const override;
183
184 std::optional<ContactInfo> tryGetContactInfo() const override;
185
186 LaunchReason launchReason() const override;
187
188private:
189 struct JsScopeData
190 {
191 QNapi::Reference<QNapi::Object> want;
192 };
193
194 std::shared_ptr<JsScopeData> m_jsScopeData;
195 QJsonObject m_jsonObject;
196 LaunchReason m_launchReason;
197};
198
199WantInfoImpl::WantInfoImpl(QNapi::Object want, LaunchReason launchReason)
201 , m_jsScopeData(
202 QtOhos::makeProxyWithJsThreadDeleter(
203 QtOhos::moveToSharedPtr(
204 JsScopeData {
205 .want = QNapi::Reference<>::makePersistentFrom(want),
206 })))
207 , m_jsonObject(QOhosJsEnv::fromNapiValue<QJsonObject>(want))
208 , m_launchReason(launchReason)
209{
210}
211
212QJsonObject WantInfoImpl::jsonObject() const
213{
214 return m_jsonObject;
215}
216
217std::optional<QList<detail::SharedRecord>> WantInfoImpl::tryGetSharedDataRecords() const
218{
219 using SharedRecord = detail::SharedRecord;
220
221 return QOhosJsThreadGateway::evalWithPromise<std::optional<QList<SharedRecord>>>(
222 [&](QOhosJsState &jsState, QOhosTaskPromise<std::optional<QList<SharedRecord>>> evalPromise) {
223 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
224 jsState.evalToPromiseOrRejectOnThrow(
225 "@kit.ShareKit.systemShare.getSharedData(*)", {m_jsScopeData->want.Value()})
226 .onThen(
227 [thenPromise = std::move(thenCatchPromises.first)](const QOhosCallbackInfo &cbInfo) {
228 QNapi::Object sharedData = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
229
230 auto optRecords = QNapi::getArrayElements<QList<std::optional<SharedRecord>>, QNapi::Object>(
231 sharedData.eval<QNapi::Array>("getRecords()"), &tryConvertNapiObjectToSharedRecord);
232
233 QList<SharedRecord> records;
234 for (const auto &optRecord : optRecords) {
235 if (optRecord.has_value())
236 records.append(optRecord.value());
237 }
238
239 std::size_t unconvertedRecordsCount = optRecords.size() - records.size();
240 if (unconvertedRecordsCount != 0) {
241 qOhosPrintfWarning(
242 "%s: can't convert %zu Shared Records, ignoring them",
243 Q_FUNC_INFO, unconvertedRecordsCount);
244 }
245
246 thenPromise(std::make_optional(records));
247 })
248 .onCatch(
249 [catchPromise = std::move(thenCatchPromises.second)](const QOhosCallbackInfo &cbInfo) {
250 QtOhos::logJsCallbackError(cbInfo, "@kit.ShareKit.systemShare.getSharedData() failed");
251 catchPromise(std::nullopt);
252 });
253 },
254 Q_FUNC_INFO);
255}
256
257std::optional<detail::WantInfoPriv::ContactInfo> WantInfoImpl::tryGetContactInfo() const
258{
259 using ContactInfo = detail::WantInfoPriv::ContactInfo;
260
261 return QOhosJsThreadGateway::evalWithPromise<std::optional<ContactInfo>>(
262 [&](QOhosJsState &jsState, QOhosTaskPromise<std::optional<ContactInfo>> evalPromise) {
263 auto thenCatchPromises = std::move(evalPromise).makeThenCatchBranches(Q_FUNC_INFO);
264 jsState.evalToPromiseOrRejectOnThrow(
265 "@kit.ShareKit.systemShare.getContactInfo(*)", {m_jsScopeData->want.Value()})
266 .onThen(
267 [thenPromise = std::move(thenCatchPromises.first)](const QOhosCallbackInfo &cbInfo) {
268 auto contactInfoObj = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
269 ContactInfo contactInfo = {
270 .contactType = QString::fromStdString(
271 contactInfoObj.get<QNapi::String>("contactType")),
272 .contactId = QString::fromStdString(
273 contactInfoObj.get<QNapi::String>("contactId")),
274 };
275 thenPromise(std::make_optional(contactInfo));
276 })
277 .onCatch(
278 [catchPromise = std::move(thenCatchPromises.second)](const QOhosCallbackInfo &cbInfo) {
279 QtOhos::logJsCallbackError(cbInfo, "@kit.ShareKit.systemShare.getContactInfo() failed");
280 catchPromise(std::nullopt);
281 });
282 },
283 Q_FUNC_INFO);
284}
285
286detail::WantInfoPriv::LaunchReason WantInfoImpl::launchReason() const
287{
288 return m_launchReason;
289}
290
291}
292
294{
295 return QOhosJsThreadGateway::eval(
296 [](QOhosJsState &jsState) {
297 auto optAppLaunchParam = jsState.optAppLaunchParam();
298 auto optAppLaunchReason = optAppLaunchParam.has_value()
299 ? std::make_optional(mapJsLaunchReasonToWantInfoEnumWithFallback(
300 jsState, optAppLaunchParam.value().get<QNapi::Number>("launchReason")))
301 : std::nullopt;
302 auto appLaunchReason = optAppLaunchReason.value_or(detail::WantInfoPriv::LaunchReason::UNKNOWN);
303 return QSharedPointer<WantInfoImpl>::create(jsState.appLaunchWant(), appLaunchReason);
304 },
305 Q_FUNC_INFO);
306}
307
308void addNewWantConsumer(QObject *context, QOhosConsumer<QJsonObject> wantConsumer)
309{
310 auto sharedWantConsumer = QtOhos::moveToSharedPtr(std::move(wantConsumer));
311 addNewWantConsumer(
312 context,
313 [sharedWantConsumer](QSharedPointer<detail::WantInfoPriv> wantInfo) {
314 (*sharedWantConsumer)(wantInfo->jsonObject());
315 });
316}
317
319 QObject *context, QOhosConsumer<QSharedPointer<detail::WantInfoPriv>> wantConsumer)
320{
321 auto contextRef = QtOhos::makeQThreadSafeRef(context);
322 auto sharedWantConsumer = QtOhos::moveToSharedPtr(std::move(wantConsumer));
323 QOhosJsThreadGateway::runAndWait(
324 [&](QOhosJsState &jsState) {
325 jsState.addNewWantConsumer(
326 [contextRef, sharedWantConsumer](QOhosJsState &jsState, QNapi::Object napiWant, QNapi::Object launchParam) {
327 auto launchReason = mapJsLaunchReasonToWantInfoEnumWithFallback(
328 jsState, launchParam.get<QNapi::Number>("launchReason"));
329 auto wantInfo = QSharedPointer<WantInfoImpl>::create(napiWant, launchReason);
330 contextRef.visitInQtThreadIfAlive(
331 [sharedWantConsumer, wantInfo](auto &) {
332 (*sharedWantConsumer)(wantInfo);
333 });
334 });
335 },
336 Q_FUNC_INFO);
337}
338
339}
340
341QT_END_NAMESPACE
void addNewWantConsumer(QObject *context, QOhosConsumer< QJsonObject > wantConsumer)
QSharedPointer< detail::WantInfoPriv > makeAppLaunchWantInfo()