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