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
qohossharekitbackend.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/qohosjsenv_p.h>
7
8#include <QtCore/private/qcore_ohos_p.h>
9#include <QtCore/private/qnapi_p.h>
10#include <QtCore/private/qohoscommon_p.h>
11#include <QtCore/private/qohosjstools_p.h>
12#include <QtCore/private/qohoslogger_p.h>
13#include <QtCore/private/qohospathutils_p.h>
14
15#include <cstdint>
16#include <cstring>
17#include <functional>
18#include <memory>
19#include <optional>
20#include <string>
21#include <vector>
22
23#include <database/udmf/udmf_meta.h>
24#include <database/udmf/utd.h>
25
26QT_BEGIN_NAMESPACE
27
28namespace QtHarmonyExtras::Private {
29
30namespace QOhosShareKit {
31
32namespace {
33
34std::optional<std::string> tryMapMimeTypeToUtdTypeId(const std::string &mimeType)
35{
36 // FIXME: the `utdGetTypesByMimeType()` result interpretation is not quite clear.
37 // Documentaion does not mention why there is a list of possible type ids.
38 // Are they ordered in a specific manear? Is it possible to get en ampty list?
39 // Improve and fix the code when these are known.
40 unsigned int typesCount = 0;
41 const char **utdTypeIds = ::OH_Utd_GetTypesByMimeType(mimeType.c_str(), &typesCount);
42 if (utdTypeIds == nullptr && typesCount != 0) {
43 qOhosReportFatalErrorAndAbort(
44 "%s: got inconsistent result from OH_Utd_GetTypesByMimeType() call: array is null, size is %u",
45 Q_FUNC_INFO, typesCount);
46 }
47
48 auto utdTypeIdsList = utdTypeIds != nullptr
49 ? std::vector<std::string>(utdTypeIds, utdTypeIds + typesCount)
50 : std::vector<std::string>();
51
52 ::OH_Utd_DestroyStringList(utdTypeIds, typesCount);
53
54 return !utdTypeIdsList.empty()
55 ? std::make_optional(utdTypeIdsList.front())
56 : std::nullopt;
57}
58
59std::optional<QNapi::Object> tryMakeShareKitSharedDataRecordObject(
60 QOhosJsState &jsState, const SharedRecord &record)
61{
62 auto optUtdType = record.mimeType != mimeTextUriList
63 ? tryMapMimeTypeToUtdTypeId(record.mimeType)
64 : std::optional<std::string>(UDMF_META_HYPERLINK);
65
66 if (!optUtdType.has_value()) {
67 qOhosPrintfWarning(
68 "%s: Cannot convert mime type: %s to utd type.", Q_FUNC_INFO, record.mimeType.c_str());
69 return std::nullopt;
70 }
71
72 std::vector<std::pair<std::string, QNapi::ValueWrapper>> objectProperties = {
73 {"utd", optUtdType.value()},
74 };
75
76 if (record.content.has_value()) {
77 objectProperties.emplace_back("content", record.content.value());
78 } else if (record.filePath.has_value()) {
79 objectProperties.emplace_back(
80 "uri",
81 tryMapPathToOhosFileUri(record.filePath.value()).value_or(""));
82 } else {
83 qOhosPrintfWarning("%s: Record doesn't have content nor uri.", Q_FUNC_INFO);
84 return std::nullopt;
85 }
86
87 if (record.title.has_value())
88 objectProperties.emplace_back("title", record.title.value());
89
90 if (record.label.has_value())
91 objectProperties.emplace_back("label", record.label.value());
92
93 if (record.description.has_value())
94 objectProperties.emplace_back("description", record.description.value());
95
96 if (record.thumbnail.has_value()) {
97 auto thumbnail = record.thumbnail.value();
98 auto napiThumbnail = QNapi::TypedArrayOf<std::uint8_t>::New(jsState.env(), thumbnail.length());
99 std::memcpy(napiThumbnail.Data(), thumbnail.data(), thumbnail.length());
100
101 objectProperties.emplace_back("thumbnail", napiThumbnail);
102 }
103
104 if (record.thumbnailFilePath.has_value()) {
105 objectProperties.emplace_back(
106 "thumbnailUri",
107 tryMapPathToOhosFileUri(record.thumbnailFilePath.value()).value_or(""));
108 }
109
110 if (record.extraData.has_value()) {
111 objectProperties.emplace_back(
112 "extraData",
113 QOhosJsEnv::toNapiValue(jsState.env(), QJsonObject::fromVariantMap(record.extraData.value())));
114 }
115
116 return std::make_optional(QNapi::makeObject(jsState.env(), objectProperties));
117}
118
119std::optional<QNapi::Object> tryMakeShareKitShareControllerAnchorObject(
120 QOhosJsState &jsState, const ShareControllerAnchor &anchor)
121{
122 if (anchor.windowOffset.isNull())
123 return std::nullopt;
124
125 auto shareControllerAnchorObject = QNapi::makeObject(
126 jsState.env(),
127 {
128 {
129 "windowOffset",
130 QNapi::makeObject(
131 jsState.env(),
132 {
133 {"x", anchor.windowOffset.x()},
134 {"y", anchor.windowOffset.y()},
135 })
136 },
137 });
138
139 if (anchor.size.has_value() && anchor.size.value().isValid()) {
140 shareControllerAnchorObject.set(
141 "size",
142 QNapi::makeObject(
143 jsState.env(),
144 {
145 {"width", anchor.size.value().width()},
146 {"height", anchor.size.value().height()},
147 }));
148 }
149
150 return std::make_optional(shareControllerAnchorObject);
151}
152
153QNapi::Object makeShareKitControllerOptionsObject(
154 QOhosJsState &jsState, ControllerOptions controllerOptions)
155{
156 auto controllerOptionsObject = QNapi::makeObject(jsState.env());
157
158 if (controllerOptions.selectionMode.has_value()) {
159 controllerOptionsObject.set(
160 "selectionMode", jsState.mapOhosEnumToJs(controllerOptions.selectionMode.value()));
161 }
162
163 if (controllerOptions.anchor.has_value()) {
164 auto controllerAnchorObject = tryMakeShareKitShareControllerAnchorObject(
165 jsState, controllerOptions.anchor.value());
166 if (controllerAnchorObject.has_value())
167 controllerOptionsObject.set("anchor", controllerAnchorObject.value());
168 }
169
170 if (controllerOptions.previewMode.has_value()) {
171 controllerOptionsObject.set(
172 "previewMode", jsState.mapOhosEnumToJs(controllerOptions.previewMode.value()));
173 }
174
175 if (controllerOptions.excludedAbilities.has_value()) {
176 controllerOptionsObject.set(
177 "excludedAbilities",
178 QNapi::makeArray(
179 jsState.env(), controllerOptions.excludedAbilities.value(),
180 [&](auto excludedAbilityType) {
181 return jsState.mapOhosEnumToJs(excludedAbilityType);
182 }));
183 }
184
185 return controllerOptionsObject;
186}
187
188std::shared_ptr<void> registerOnOffShareCompletedEventHandler(
189 QNapi::Object shareController, QOhosConsumer<ShareOperationResult> shareCompletedCallback)
190{
191 return registerQOhosOnOffMethodsBasedEventHandler(
192 shareController, "shareCompleted",
193 [shareCompletedCallback = std::move(shareCompletedCallback)](const QNapi::CallbackInfo &cbInfo) {
194 const auto shareOperationResult = cbInfo.getFirstArg<QNapi::Object>(Q_FUNC_INFO);
195 const auto targetAbilityName = shareOperationResult.eval<QNapi::String>(
196 "targetAbilityInfo.name");
197 shareCompletedCallback(
199 .targetAbilityName = targetAbilityName,
200 });
201 });
202}
203
204void shareDataImpl(
205 QOhosJsState &jsState, QNapi::Object uiAbility,
206 const std::vector<SharedRecord> &recordsToShare, ControllerOptions controllerOptions,
207 std::function<void()> panelClosedCallback, QOhosConsumer<ShareOperationResult> shareCompletedCallback,
208 QOhosConsumer<std::shared_ptr<void>> resultConsumer)
209{
210 qOhosPrintfDebug("%s: sharing %lu records through ShareKit", Q_FUNC_INFO, recordsToShare.size());
211
212 if (recordsToShare.empty()) {
213 qOhosPrintfWarning("%s: No records to share. Skipping...", Q_FUNC_INFO);
214 resultConsumer(nullptr);
215 return;
216 }
217
218 auto firstRecord = tryMakeShareKitSharedDataRecordObject(jsState, recordsToShare.front());
219 if (!firstRecord.has_value()) {
220 qOhosPrintfWarning("%s: Failed to create the very first record, skip sharing", Q_FUNC_INFO);
221 resultConsumer(nullptr);
222 return;
223 }
224 qOhosPrintfDebug(
225 "%s: record #0 to be shared: %s",
226 Q_FUNC_INFO, QNapi::toJsonString(firstRecord.value()).c_str());
227
228 auto sharedDataObject = jsState.eval<QNapi::Object>(
229 "@kit.ShareKit.systemShare.SharedData<new>(*)", {firstRecord.value()});
230
231 for (std::size_t i = 1; i < recordsToShare.size(); i++) {
232 auto record = tryMakeShareKitSharedDataRecordObject(jsState, recordsToShare.at(i));
233 if (record.has_value()) {
234 qOhosPrintfDebug(
235 "%s: record #%zu to be shared: %s",
236 Q_FUNC_INFO, i, QNapi::toJsonString(record.value()).c_str());
237 sharedDataObject.eval("addRecord(*)", {record.value()});
238 } else {
239 qOhosPrintfWarning("%s: Failed to create record", Q_FUNC_INFO);
240 }
241 }
242
243 auto controller = jsState.eval<QNapi::Object>(
244 "@kit.ShareKit.systemShare.ShareController<new>(*)", {sharedDataObject});
245
246 auto callbacksHandle = QtOhos::moveToSharedPtr(
247 std::vector<std::shared_ptr<void>>{
248 registerQOhosOnOffMethodsBasedEventHandler(
249 controller, "dismiss", std::move(panelClosedCallback)),
250 registerOnOffShareCompletedEventHandler(controller, std::move(shareCompletedCallback)),
251 });
252
253 controller.evalToPromiseOrRejectOnThrow(
254 "show(*)",
255 {
256 uiAbility.get<QNapi::Object>("context"),
257 makeShareKitControllerOptionsObject(jsState, controllerOptions),
258 })
259 .withContext(std::move(resultConsumer))
260 .onThenWithContext(
261 [callbacksHandle](auto &resultConsumer) {
262 resultConsumer(callbacksHandle);
263 })
264 .onCatchWithContext(
265 [](const QOhosCallbackInfo &cbInfo, auto &resultConsumer) {
266 QtOhos::logJsCallbackError(cbInfo, "ShareController.show()");
267 resultConsumer(nullptr);
268 });
269}
270
271template<typename ...Ts>
272QOhosConsumer<Ts...> makeAsyncConsumer(
273 QOhosConsumer<Ts...> baseConsumer, QOhosConsumer<std::function<void()>> asyncExecutor)
274{
275 auto sharedBaseConsumer = QtOhos::moveToSharedPtr(std::move(baseConsumer));
276 return [sharedBaseConsumer, asyncExecutor = std::move(asyncExecutor)](const Ts &...args) {
277 asyncExecutor(
278 [sharedBaseConsumer, args...]() mutable {
279 (*sharedBaseConsumer)(std::move(args)...);
280 });
281 };
282}
283
284}
285
286std::shared_ptr<void> shareData(
287 QWindow *optInstanceMainWindow, const std::vector<SharedRecord> &recordsToShare,
288 ControllerOptions controllerOptions, std::function<void()> panelClosedCallback,
289 QOhosConsumer<ShareOperationResult> shareCompletedCallback)
290{
291 auto optMainWindowInstanceObjectRef =
292 optInstanceMainWindow != nullptr
293 ? std::make_optional(QtOhos::QObjectThreadSafeRef(optInstanceMainWindow))
294 : std::nullopt;
295
296 auto panelClosedJsCallback = makeAsyncConsumer(
297 std::move(panelClosedCallback), &QtOhos::invokeInQtThread);
298 auto shareCompletedJsCallback = makeAsyncConsumer(
299 std::move(shareCompletedCallback), &QtOhos::invokeInQtThread);
300
301 return QOhosJsThreadGateway::evalWithPromise<std::shared_ptr<void>>(
302 [&](QOhosJsState &jsState, QOhosTaskPromise<std::shared_ptr<void>> evalPromise) {
303 auto optUiAbility = optMainWindowInstanceObjectRef.has_value()
304 ? jsState.tryGetQAbilityByQWindow(optMainWindowInstanceObjectRef.value())
305 : jsState.defaultQAbility();
306 if (!optUiAbility.has_value()) {
307 qOhosPrintfWarning("%s: no UIAbility available to share data with ShareKit", Q_FUNC_INFO);
308 evalPromise(nullptr);
309 return;
310 }
311
312 shareDataImpl(
313 jsState, optUiAbility.value(), recordsToShare, controllerOptions, std::move(panelClosedJsCallback),
314 std::move(shareCompletedJsCallback), std::move(evalPromise));
315 },
316 Q_FUNC_INFO);
317}
318
319}
320
321}
322
323QT_END_NAMESPACE
std::shared_ptr< void > shareData(QWindow *optInstanceMainWindow, const std::vector< SharedRecord > &recordsToShare, ControllerOptions controllerOptions, std::function< void()> panelClosedCallback, QOhosConsumer< ShareOperationResult > shareCompletedCallback)