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