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
qohosudmfconversions.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
4#include <QtCore/private/qohoscommon_p.h>
5#include <QtCore/private/qohoslogger_p.h>
6#include <QtCore/private/qohospathutils_p.h>
7#include <QtCore/qspan.h>
8#include <QtCore/qstringlist.h>
9#include <QtCore/qurl.h>
10#include <QtGui/private/qohosimageconversions_p.h>
11#include <QtGui/qimage.h>
12#include <algorithm>
13#include <chrono>
14#include <cstring>
15#include <database/udmf/udmf_err_code.h>
16#include <database/udmf/udmf_meta.h>
17#include <database/udmf/utd.h>
18#include <qarkui/qarkuiutils.h>
19#include <qohosmimedata.h>
20#include <qohospixelmapconversions.h>
21#include <qohosplugincore.h>
22#include <qohosudmfconversions.h>
23#include <qohosudmf.h>
24#include <qohosudsobject.h>
25#include <qohosutils.h>
26#include <random>
27#include <unistd.h>
28
29namespace ch = std::chrono;
30
31QT_BEGIN_NAMESPACE
32
33namespace {
34
35constexpr const char *mimeTextPlain = "text/plain";
36constexpr const char *mimeTextHtml = "text/html";
37constexpr const char *mimeTextUriList = "text/uri-list";
38constexpr const char *mimeAppXQtImage = "application/x-qt-image";
39
40const auto qtAppInfoDataPseudoMimeType = "io.qt.ohos.appInfoData";
41const auto qtDummyEntryPseudoMimeType = "io.qt.ohos.dummyEntry";
42
43constexpr auto processQMimeDataInQtThreadTimeout = ch::seconds(2);
44
46{
47public:
48 template<typename RawUdsObject>
50 QOhosSupplier<QOhosUdsObject<RawUdsObject>> udsObjectFactory);
51
53 std::string mimeType, QOhosSupplier<QSpan<const std::uint8_t>> dataSupplier);
54
56
57 void addEntryToRecord(QOhosUdmfRecord &udmfRecord) const;
58
60
61private:
63 QOhosSupplier<void *> recordProviderDataFactory,
64 std::function<void(QOhosUdmfRecord &)> recordAddEntryFunc);
65
66 QOhosSupplier<void *> m_recordProviderDataFactory;
67 std::function<void(QOhosUdmfRecord &)> m_recordAddEntryFunc;
68};
69
75
77{
78 static std::vector<std::uint8_t> appInfoData = []() {
79 std::vector<std::uint8_t> appInfoData;
80
81 auto pidString = std::to_string(::getpid()) + ":";
82 std::copy(pidString.begin(), pidString.end(), std::back_inserter(appInfoData));
83
84 using RandomBytesEngine = std::independent_bits_engine<std::default_random_engine, 8, std::uint8_t>;
85 std::vector<std::uint8_t> key(64);
86 std::generate(std::begin(key), std::end(key), RandomBytesEngine(std::random_device()()));
87 std::copy(key.begin(), key.end(), std::back_inserter(appInfoData));
88
89 return appInfoData;
90 }();
91
92 return appInfoData;
93}
94
95template<typename T>
97 std::function<void(QOhosConsumer<T>)> qtEvalFunc, ch::nanoseconds timeout)
98{
99 auto sharedResultBox = std::make_shared<std::optional<T>>();
100
101 auto qtTaskFinished = QtOhos::tryInvokeInQtThreadAndTryWaitForContinue(
102 [sharedResultBox, qtEvalFunc = std::move(qtEvalFunc)](std::function<void()> continueFunc) {
103 qtEvalFunc(
104 [sharedResultBox, continueFunc = std::move(continueFunc)](T result) {
105 *sharedResultBox = std::move(result);
106 continueFunc();
107 });
108 },
109 timeout);
110
111 return qtTaskFinished
112 ? std::move(*sharedResultBox)
113 : std::nullopt;
114}
115
116template<typename T, typename K>
117std::vector<T> getMapKeys(const std::map<T, K> &inputMap)
118{
119 std::vector<T> mapKeys;
120 mapKeys.reserve(inputMap.size());
121 for (const auto &mapEntry : inputMap)
122 mapKeys.push_back(mapEntry.first);
123 return mapKeys;
124}
125
126template<typename T>
128{
129 return [value = std::move(value)]() {
130 return std::optional(value);
131 };
132}
133
134bool hasQMimeDataPeerType(const char *type)
135{
136 static constexpr const char *udmfTypesWithQMimeDataPeerTypes[] = {
137 QOhosUdsMeta<::OH_UdsFileUri>::udmfMetaId,
138 QOhosUdsMeta<::OH_UdsHyperlink>::udmfMetaId,
139 QOhosUdsMeta<::OH_UdsHtml>::udmfMetaId,
140 QOhosUdsMeta<::OH_UdsPixelMap>::udmfMetaId,
141 QOhosUdsMeta<::OH_UdsPlainText>::udmfMetaId,
142 };
143
144 return std::any_of(
145 std::begin(udmfTypesWithQMimeDataPeerTypes),
146 std::end(udmfTypesWithQMimeDataPeerTypes),
147 [&](const char *specificMetaType) {
148 return std::strcmp(type, specificMetaType) == 0;
149 });
150}
151
152bool isUdmfMetaFileType(const std::string &type)
153{
154 static constexpr const char *udmfMetaFileTypes[] = {
155 UDMF_META_GENERAL_FILE,
156 UDMF_META_AUDIO,
157 UDMF_META_FOLDER,
158 UDMF_META_IMAGE,
159 UDMF_META_VIDEO,
160 };
161
162 return std::any_of(
163 std::begin(udmfMetaFileTypes),
164 std::end(udmfMetaFileTypes),
165 [&](const char *specificMetaType) {
166 return type == specificMetaType;
167 });
168}
169
170std::vector<std::string> utdGetTypesByMimeType(const std::string &mimeType)
171{
172 unsigned int typesCount = 0;
173 auto func = Q_OHOS_NAMED_FUNC(::OH_Utd_GetTypesByMimeType);
174 const char **utdTypeIds = QArkUi::callArkUi(func, QArkUi::CZString(mimeType.c_str()), &typesCount);
175 if (utdTypeIds == nullptr && typesCount != 0) {
176 qOhosReportFatalErrorAndAbort(
177 "%s: got inconsistent result from %s() call: array is null, size is %u",
178 Q_FUNC_INFO, func.name(), typesCount);
179 }
180
181 auto result = utdTypeIds != nullptr
182 ? std::vector<std::string>(utdTypeIds, utdTypeIds + typesCount)
183 : std::vector<std::string>();
184
185 QArkUi::callArkUi(
186 Q_OHOS_NAMED_FUNC(::OH_Utd_DestroyStringList), utdTypeIds, typesCount);
187
188 return result;
189}
190
191std::shared_ptr<::OH_Utd> utdCreateOrNull(const std::string &typeId)
192{
193 return {
194 QArkUi::callArkUi(
195 Q_OHOS_NAMED_FUNC(::OH_Utd_Create),
196 QArkUi::CZString(typeId.c_str())),
197 [](::OH_Utd *utd) {
198 if (utd != nullptr)
199 QArkUi::callArkUi(Q_OHOS_NAMED_FUNC(::OH_Utd_Destroy), utd);
200 }
201 };
202}
203
205{
206 unsigned int typesCount = 0;
207 auto func = Q_OHOS_NAMED_FUNC(::OH_Utd_GetMimeTypes);
208 const char **mimeTypes = QArkUi::callArkUi(func, utd, &typesCount);
209 if (mimeTypes == nullptr && typesCount != 0) {
210 qOhosReportFatalErrorAndAbort(
211 "%s: got inconsistent result from %s() call: array is null, size is %u",
212 Q_FUNC_INFO, func.name(), typesCount);
213 }
214
215 auto result = mimeTypes != nullptr
216 ? std::vector<std::string>(mimeTypes, mimeTypes + typesCount)
217 : std::vector<std::string>();
218
219 return result;
220}
221
222template<typename T>
223bool hasMatchingTypeEntryInRecords(QSpan<const QOhosUdmfRecord> records)
224{
225 static const auto searchTypeId = QOhosUdsMeta<T>::udmfMetaId;
226 return std::any_of(
227 records.begin(), records.end(),
228 [&](auto &record) {
229 auto recordTypes = record.getTypes();
230 return std::find(recordTypes.begin(), recordTypes.end(), searchTypeId) != recordTypes.end();
231 });
232}
233
234template<typename T>
236 QSpan<const QOhosUdmfRecord> records,
237 const QOhosConsumer<QOhosUdsObject<T>> &processEntryFunc)
238{
239 for (auto &record : records) {
240 for (auto &typeId : record.getTypes()) {
241 if (QOhosUdsMeta<T>::udmfMetaId == typeId)
242 processEntryFunc(record.getEntry<T>());
243 }
244 }
245}
246
248 std::shared_ptr<void> context, QSpan<const QOhosUdmfRecord> inputRecords,
250{
251 if (!hasMatchingTypeEntryInRecords<::OH_UdsFileUri>(inputRecords)
252 && !hasMatchingTypeEntryInRecords<::OH_UdsHyperlink>(inputRecords)) {
253 return;
254 }
255
256 outMimeDataSuppliers.emplace(
257 QString::fromUtf8(mimeTextUriList),
258 [context, inputRecords]() {
259 QList<QUrl> urls;
260 tryProcessEntriesOfTypeInRecords<::OH_UdsFileUri>(
261 inputRecords,
262 [&](auto udsEntry) {
263 urls.append(
264 {
265 QUrl::fromLocalFile(
266 QString::fromStdString(
267 tryMapOhosFileUriToPath(
268 udsEntry.getContent()).value_or(""))),
269 });
270 });
271 tryProcessEntriesOfTypeInRecords<::OH_UdsHyperlink>(
272 inputRecords,
273 [&](auto udsEntry) {
274 urls.append(QUrl(QString::fromStdString(udsEntry.getContent())));
275 });
276
277 QList<QVariant> urlsVariants;
278 for (const auto &url : urls)
279 urlsVariants.append(url);
280
281 return QVariant(urlsVariants);
282 });
283}
284
286 std::shared_ptr<void> context, QSpan<const QOhosUdmfRecord> inputRecords,
288{
289 auto qtDummyEntryUtdTypeId = tryMapMimeTypeToUtdTypeId(qtDummyEntryPseudoMimeType);
290 if (!qtDummyEntryUtdTypeId.has_value()) {
291 qOhosPrintfWarning("%s: Can't map %s to UTD type id, ignoring", Q_FUNC_INFO, qtDummyEntryPseudoMimeType);
292 }
293
294 for (auto &record : inputRecords) {
295 for (auto &typeId : record.getTypes()) {
296 if (typeId == qtDummyEntryUtdTypeId)
297 continue;
298
299 if (!hasQMimeDataPeerType(typeId.c_str()) && !isUdmfMetaFileType(typeId)) {
300 // FIXME: the `utdGetMimeTypes()` result interpretation is not quite
301 // clear. Documentaion does not mention why there is a list of possible mime types.
302 // Are they ordered in a specific manear? Is it possible to get en ampty list?
303 // Improve and fix the code when these are known.
304 auto optUtdWithTypeId = utdCreateOrNull(typeId);
305 auto mimeTypes = optUtdWithTypeId
306 ? utdGetMimeTypes(optUtdWithTypeId.get())
307 : std::vector<std::string>();
308 if (!mimeTypes.empty()) {
309 outMimeDataSuppliers.emplace(
310 QString::fromStdString(mimeTypes.front()),
311 [context, recordPtr = &record, typeId]() {
312 auto generalEntry = recordPtr->tryGetGeneralEntry(typeId);
313 if (!generalEntry.has_value())
314 return QVariant();
315
316 return QVariant(
317 QByteArray(
318 reinterpret_cast<const char *>(generalEntry.value().data()),
319 generalEntry.value().size()));
320 });
321 } else {
322 qOhosPrintfWarning(
323 "%s: cannot convert UTD type id '%s' to mime type. Skip this entry.",
324 Q_FUNC_INFO, typeId.c_str());
325 }
326 }
327 }
328 }
329}
330
331std::map<QString, QOhosSupplier<QVariant>> makeMimeDataSuppliersMapFromUdmfData(QOhosUdmfData udmfData)
332{
333 struct Context
334 {
335 QOhosUdmfData udmfData;
336 std::vector<QOhosUdmfRecord> records;
337 };
338
339 auto context = QtOhos::makeProxyWithJsThreadDeleter(std::make_shared<Context>());
340 context->udmfData = std::move(udmfData);
341 context->records = context->udmfData.getRecords();
342
343 if (context->records.size() == 0)
344 return {};
345
346 std::map<QString, QOhosSupplier<QVariant>> mimeDataSuppliers;
347
348 auto firstRecordSpan = QSpan(context->records.data(), 1);
349
350 if (hasMatchingTypeEntryInRecords<::OH_UdsPlainText>(firstRecordSpan)) {
351 mimeDataSuppliers.emplace(
352 QString::fromUtf8(mimeTextPlain),
353 [context]() {
354 QVariant value;
355 tryProcessEntriesOfTypeInRecords<::OH_UdsPlainText>(
356 QSpan(context->records.data(), 1),
357 [&](auto udsEntry) {
358 value = QString::fromUtf8(udsEntry.getContent(), -1);
359 });
360 return value;
361 });
362 }
363
364 if (hasMatchingTypeEntryInRecords<::OH_UdsHtml>(firstRecordSpan)) {
365 mimeDataSuppliers.emplace(
366 QString::fromUtf8(mimeTextHtml),
367 [context]() {
368 QVariant value;
369 tryProcessEntriesOfTypeInRecords<::OH_UdsHtml>(
370 QSpan(context->records.data(), 1),
371 [&](auto udsEntry) {
372 value = QString::fromUtf8(udsEntry.getContent(), -1);
373 });
374 return value;
375 });
376 }
377
378 if (hasMatchingTypeEntryInRecords<::OH_UdsPixelMap>(firstRecordSpan)) {
379 mimeDataSuppliers.emplace(
380 QString::fromUtf8(mimeAppXQtImage),
381 [context]() {
382 QVariant value;
383 tryProcessEntriesOfTypeInRecords<::OH_UdsPixelMap>(
384 QSpan(context->records.data(), 1),
385 [&](auto udsEntry) {
386 value = createQImageFromNativePixelMap(udsEntry.getContent().get());
387 });
388 return value;
389 });
390 }
391
392 addMimeDataSuppliersForUrlLikeEntriesFromRecords(
393 context, QSpan(context->records.data(), context->records.size()), mimeDataSuppliers);
394
395 addMimeDataSuppliersForGeneralEntriesFromRecords(context, firstRecordSpan, mimeDataSuppliers);
396
397 return mimeDataSuppliers;
398}
399
401{
402 return url.isLocalFile()
403 ? UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsFileUri>(
404 [url]() {
405 return QOhosUdsObject<::OH_UdsFileUri>(
406 tryMapPathToOhosFileUri(
407 url.toLocalFile().toStdString()).value_or("").c_str());
408 })
409 : UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsHyperlink>(
410 [url]() {
411 return QOhosUdsObject<::OH_UdsHyperlink>(
412 url.toString().toStdString().c_str());
413 });
414}
415
416void addUrlEntryToUdmfRecord(const QUrl &url, QOhosUdmfRecord &outRecord)
417{
418 makeUdmfRecordEntryFactoryForUrl(url).addEntryToRecord(outRecord);
419}
420
422{
423 return {
424 QArkUi::callArkUiOrFailOnNullResult(
425 Q_OHOS_NAMED_FUNC(::OH_UdmfProperty_Create),
426 udmfData),
427 [](::OH_UdmfProperty *udmfProperty) {
428 QArkUi::callArkUi(
429 Q_OHOS_NAMED_FUNC(::OH_UdmfProperty_Destroy),
430 udmfProperty);
431 }
432 };
433}
434
436{
437 return {
438 QArkUi::callArkUiOrFailOnNullResult(
439 Q_OHOS_NAMED_FUNC(::OH_UdsArrayBuffer_Create)),
440 [](::OH_UdsArrayBuffer *udsArrayBuffer) {
441 QArkUi::callArkUiOrFailOnErrorResult(
442 Q_OHOS_NAMED_FUNC(::OH_UdsArrayBuffer_Destroy),
443 udsArrayBuffer);
444 }
445 };
446}
447
449 createUdsArrayBufferWithData(QSpan<const std::uint8_t> data)
450{
451 auto udsArrayBuffer = createUdsArrayBuffer();
452 QArkUi::callArkUiOrFailOnErrorResult(
453 Q_OHOS_NAMED_FUNC(::OH_UdsArrayBuffer_SetData),
454 udsArrayBuffer.get(), const_cast<std::uint8_t *>(data.data()), data.size());
455 return udsArrayBuffer;
456}
457
458void addGeneralEntryToRecord(std::string mimeType, QSpan<const std::uint8_t> dataBytes, QOhosUdmfRecord &record)
459{
460 if (dataBytes.size() == 0) {
461 qOhosPrintfWarning(
462 "%s: got empty data for type '%s', which is not supported by UDMF record, skipping this entry",
463 Q_FUNC_INFO, mimeType.c_str());
464 return;
465 }
466
467 auto optUtdTypeId = tryMapMimeTypeToUtdTypeId(mimeType);
468 if (optUtdTypeId.has_value()) {
469 record.addGeneralEntry(optUtdTypeId.value(), dataBytes);
470 } else {
471 qOhosPrintfWarning(
472 "%s: cannot convert mime type '%s' to type id. Skip this entry.",
473 Q_FUNC_INFO, mimeType.c_str());
474 }
475}
476
478{
479 const std::uint8_t dummyEntryDataByte = 0;
480 addGeneralEntryToRecord(
481 qtDummyEntryPseudoMimeType, QSpan(&dummyEntryDataByte, 1), record);
482}
483
484template<typename RawUdsObject>
485UdmfRecordEntryFactory UdmfRecordEntryFactory::makeForUdsObjectFactory(
486 QOhosSupplier<QOhosUdsObject<RawUdsObject>> udsObjectFactory)
487{
488 auto sharedUdsObjectFactory = QtOhos::moveToSharedPtr(std::move(udsObjectFactory));
489 return UdmfRecordEntryFactory(
490 [sharedUdsObjectFactory]() {
491 void *data = (*sharedUdsObjectFactory)().release();
492 qOhosPrintfWarning(
493 "%s: generated '%s' data for OH_UdmfRecordProvider: %p",
494 Q_FUNC_INFO, QOhosUdsMeta<RawUdsObject>::udmfMetaId, data);
495 return data;
496 },
497 [sharedUdsObjectFactory](QOhosUdmfRecord &record) {
498 record.addEntry((*sharedUdsObjectFactory)());
499 });
500}
501
502UdmfRecordEntryFactory UdmfRecordEntryFactory::makeForArrayBufferWithMimeType(
503 std::string mimeType, QOhosSupplier<QSpan<const std::uint8_t>> dataSupplier)
504{
505 auto sharedDataSupplier = QtOhos::moveToSharedPtr(std::move(dataSupplier));
506 return UdmfRecordEntryFactory(
507 [mimeType, sharedDataSupplier]() {
508 auto dataSpan = (*sharedDataSupplier)();
509 auto udsArrayBuffer = createUdsArrayBufferWithData(dataSpan);
510 qOhosPrintfDebug(
511 "%s: generated array buffer for OH_UdmfRecordProvider, mimeType='%s', size=%zu",
512 Q_FUNC_INFO, mimeType.c_str(), static_cast<std::size_t>(dataSpan.size()));
513 return udsArrayBuffer.release();
514 },
515 [mimeType, sharedDataSupplier](QOhosUdmfRecord &record) {
516 addGeneralEntryToRecord(mimeType, (*sharedDataSupplier)(), record);
517 });
518}
519
521{
522 return UdmfRecordEntryFactory(
523 []() {
524 return nullptr;
525 },
526 [](QOhosUdmfRecord &) {
527 });
528}
529
530UdmfRecordEntryFactory::UdmfRecordEntryFactory(
531 QOhosSupplier<void *> recordProviderDataFactory,
532 std::function<void(QOhosUdmfRecord &)> recordAddEntryFunc)
533 : m_recordProviderDataFactory(std::move(recordProviderDataFactory))
534 , m_recordAddEntryFunc(std::move(recordAddEntryFunc))
535{
536}
537
539{
540 m_recordAddEntryFunc(udmfRecord);
541}
542
544{
545 return m_recordProviderDataFactory();
546}
547
549{
550 std::vector<UdmfRecordEntryMetaFactory> recordEntryMetaFactories;
551 for (auto qStrFormat : mimeDataFormats) {
552 auto format = qStrFormat.toStdString();
553 if (format == mimeTextPlain) {
554 recordEntryMetaFactories.emplace_back(
555 UdmfRecordEntryMetaFactory{
556 makeOptionalValueSupplier<std::string>(QOhosUdsMeta<::OH_UdsPlainText>::udmfMetaId),
557 [](const QMimeData &mimeData) {
558 auto dataText = mimeData.text();
559 return UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsPlainText>(
560 [dataText]() {
561 return QOhosUdsObject<::OH_UdsPlainText>(dataText.toStdString().c_str());
562 });
563 }
564 });
565 } else if (format == mimeTextHtml) {
566 recordEntryMetaFactories.emplace_back(
567 UdmfRecordEntryMetaFactory{
568 makeOptionalValueSupplier<std::string>(QOhosUdsMeta<::OH_UdsHtml>::udmfMetaId),
569 [](const QMimeData &mimeData) {
570 auto dataHtml = mimeData.html();
571 return UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsHtml>(
572 [dataHtml]() {
573 return QOhosUdsObject<::OH_UdsHtml>(dataHtml.toStdString().c_str());
574 });
575 }
576 });
577 } else if (format == mimeTextUriList) {
578 recordEntryMetaFactories.emplace_back(
579 UdmfRecordEntryMetaFactory{
580 {},
581 [](const QMimeData &mimeData) {
582 auto dataUrls = mimeData.urls();
583 return !dataUrls.isEmpty()
584 ? makeUdmfRecordEntryFactoryForUrl(dataUrls.first())
585 : UdmfRecordEntryFactory::makeDummy();
586 }
587 });
588 } else if (format == mimeAppXQtImage) {
589 recordEntryMetaFactories.emplace_back(
590 UdmfRecordEntryMetaFactory{
591 makeOptionalValueSupplier<std::string>(QOhosUdsMeta<::OH_UdsPixelMap>::udmfMetaId),
592 [](const QMimeData &mimeData) {
593 auto dataImage = qvariant_cast<QImage>(mimeData.imageData());
594 return UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsPixelMap>(
595 [dataImage]() {
596 return QOhosUdsObject<::OH_UdsPixelMap>(makeOhosNativePixelMapFromQImage(dataImage));
597 });
598 }
599 });
600 } else {
601 recordEntryMetaFactories.emplace_back(
602 UdmfRecordEntryMetaFactory{
603 [qStrFormat]() {
604 return tryMapMimeTypeToUtdTypeId(qStrFormat.toStdString());
605 },
606 [qStrFormat](const QMimeData &mimeData) {
607 auto dataBytes = mimeData.data(qStrFormat);
608 return UdmfRecordEntryFactory::makeForArrayBufferWithMimeType(
609 qStrFormat.toStdString(),
610 [dataBytes]() {
611 return QSpan<const std::uint8_t>(
612 reinterpret_cast<const std::uint8_t *>(dataBytes.constData()),
613 dataBytes.length());
614 });
615 }
616 });
617 }
618 }
619
620 return recordEntryMetaFactories;
621}
622
624 const QMimeData &mimeData, std::shared_ptr<QMimeData> optLazyProcessingMimeData)
625{
626 auto recordEntryMetaFactories = makeRecordEntryMetaFactoriesForMimeDataFormats(mimeData.formats());
627 if (recordEntryMetaFactories.empty())
628 return nullptr;
629
630 struct Context
631 {
632 std::vector<UdmfRecordEntryFactory> directEntryFactories;
633 std::vector<UdmfRecordEntryMetaFactory> providerEntries;
634 };
635
636 auto context = std::make_shared<Context>();
637
638 const bool useRecordProvider = optLazyProcessingMimeData != nullptr;
639
640 for (auto &recordEntryFactoryEntry : recordEntryMetaFactories) {
641 if (useRecordProvider && recordEntryFactoryEntry.optUdmfMetaIdFactory) {
642 context->providerEntries.push_back(std::move(recordEntryFactoryEntry));
643 } else {
644 context->directEntryFactories.push_back(
645 recordEntryFactoryEntry.metaFactoryFunc(mimeData));
646 }
647 }
648
649 return [context, optLazyProcessingMimeData]() {
650 QOhosUdmfRecord record;
651 for (const auto &directEntryFactory : context->directEntryFactories)
652 directEntryFactory.addEntryToRecord(record);
653
654 auto entryMetaFactoryFuncsForProvider =
655 std::make_shared<std::map<std::string, std::function<UdmfRecordEntryFactory(const QMimeData &)>>>();
656 for (const auto &providerEntry : context->providerEntries) {
657 auto optUdmfMetaId = providerEntry.optUdmfMetaIdFactory();
658 if (optUdmfMetaId.has_value()) {
659 entryMetaFactoryFuncsForProvider->emplace(
660 optUdmfMetaId.value(), providerEntry.metaFactoryFunc);
661 }
662 }
663
664 if (!entryMetaFactoryFuncsForProvider->empty()) {
665 if (record.isEmpty())
667
668 auto lazyProcessingMimeData = optLazyProcessingMimeData;
669 record.setProviderForDataFetchFunc(
670 getMapKeys(*entryMetaFactoryFuncsForProvider),
671 [entryMetaFactoryFuncsForProvider, lazyProcessingMimeData](const char *type) {
672 qOhosPrintfDebug("%s: got request for data '%s'", Q_FUNC_INFO, type);
673
674 auto typeStr = std::string(type);
675 auto optUdmfRecordEntryFactory = tryEvalInQtThreadWithConsumer<UdmfRecordEntryFactory>(
676 [entryMetaFactoryFuncsForProvider, lazyProcessingMimeData, typeStr](QOhosConsumer<UdmfRecordEntryFactory> resultConsumer) {
677 qOhosPrintfDebug("%s: processing request for data '%s' in Qt thread", Q_FUNC_INFO, typeStr.c_str());
678 auto entryMetaFactoryFuncIter = entryMetaFactoryFuncsForProvider->find(typeStr);
679 resultConsumer(
680 entryMetaFactoryFuncIter != entryMetaFactoryFuncsForProvider->end()
681 ? entryMetaFactoryFuncIter->second(*lazyProcessingMimeData)
683 qOhosPrintfDebug("%s: finished processing in Qt thread", Q_FUNC_INFO);
684 },
685 processQMimeDataInQtThreadTimeout);
686 qOhosPrintfDebug(
687 "%s: got result from Qt thread: %s",
688 Q_FUNC_INFO, QtOhos::mapBoolToTrueFalseStr(optUdmfRecordEntryFactory.has_value()));
689
690 void *providerResult = optUdmfRecordEntryFactory.has_value()
691 ? optUdmfRecordEntryFactory.value().makeRecordProviderDataForEntry()
692 : nullptr;
693 qOhosPrintfDebug("%s: provider result: %p", Q_FUNC_INFO, providerResult);
694
695 return providerResult;
696 });
697 }
698 return record;
699 };
700}
701
703 const QMimeData &mimeData, const std::optional<bool> &shareInAppOnly,
704 std::shared_ptr<QMimeData> optLazyProcessingMimeData = nullptr)
705{
706 std::vector<std::function<QOhosUdmfRecord()>> recordFactories;
707
708 auto optDefaultRecordFactory = tryMakeDefaultUdmfRecordFactoryFromQMimeDataOrNull(
709 mimeData, optLazyProcessingMimeData);
710 if (optDefaultRecordFactory)
711 recordFactories.push_back(std::move(optDefaultRecordFactory));
712
713 if (mimeData.hasUrls()) {
714 auto urls = mimeData.urls();
715 for (int i = 1; i < urls.size(); ++i) {
716 auto dataUrl = urls.at(i);
717 recordFactories.emplace_back(
718 [dataUrl]() {
719 QOhosUdmfRecord record;
720 addUrlEntryToUdmfRecord(dataUrl, record);
721 return record;
722 });
723 }
724 }
725
726 recordFactories.emplace_back(
727 []() {
728 QOhosUdmfRecord record;
729 auto appInfoDataBytes = getAppInfoDataForThisProcess();
730 addGeneralEntryToRecord(
731 qtAppInfoDataPseudoMimeType, QSpan<const std::uint8_t>(appInfoDataBytes.data(), appInfoDataBytes.size()), record);
732 return record;
733 });
734
735 return [shareInAppOnly, recordFactories = std::move(recordFactories)]() {
736 QOhosUdmfData udmfData;
737
738 if (shareInAppOnly.has_value()) {
739 auto udmfShareProperty = createUdmfPropertyForUdmfData(udmfData.nativePtr());
740 QArkUi::callArkUiOrFailOnErrorResult(
741 Q_OHOS_NAMED_FUNC(::OH_UdmfProperty_SetShareOption),
742 udmfShareProperty.get(),
743 shareInAppOnly.value()
744 ? ::Udmf_ShareOption::SHARE_OPTIONS_IN_APP
745 : ::Udmf_ShareOption::SHARE_OPTIONS_CROSS_APP);
746 }
747
748 for (const auto &recordFactory : recordFactories)
749 udmfData.addRecord(recordFactory());
750
751 return udmfData;
752 };
753}
754
755}
756
758 const QMimeData &mimeData, const std::optional<bool> &shareInAppOnly)
759{
760 return makeUdmfDataFactoryFromQMimeDataImpl(mimeData, shareInAppOnly, nullptr);
761}
762
764 std::shared_ptr<QMimeData> mimeData, const std::optional<bool> &shareInAppOnly)
765{
766 return makeUdmfDataFactoryFromQMimeDataImpl(*mimeData, shareInAppOnly, mimeData);
767}
768
770{
771 std::map<QString, QVariant> mimeDataMap;
772 for (auto &supplierEntry : makeMimeDataSuppliersMapFromUdmfData(std::move(udmfData)))
773 mimeDataMap.emplace(supplierEntry.first, supplierEntry.second());
774
775 return makeQOhosMimeDataFactory(std::move(mimeDataMap));
776}
777
779{
780 std::map<QString, QOhosSupplier<QVariant>> threadSafeSuppliers;
781 for (auto &supplierEntry : makeMimeDataSuppliersMapFromUdmfData(std::move(udmfData))) {
782 threadSafeSuppliers.emplace(
783 supplierEntry.first,
784 [mimeType = supplierEntry.first.toStdString(), baseSupplier = std::move(supplierEntry.second)]() {
785 qOhosPrintfDebug("%s: fetching data for MIME type '%s'", Q_FUNC_INFO, mimeType.c_str());
786 return QtOhos::evalInJsThread(
787 [&](QtOhos::JsState &) {
788 return baseSupplier();
789 },
790 Q_FUNC_INFO);
791 });
792 }
793
794 return makeQOhosLazyFetchMimeDataFactory(std::move(threadSafeSuppliers));
795}
796
798 std::vector<std::string> udmfDataTypes)
799{
800 return [udmfDataTypes = std::move(udmfDataTypes)]() {
801 const auto dummyText = QString::fromUtf8(
802 "OHOS QPA: Drag data is not available before DROP. This is a system limitation.");
803
804 auto mimeData = std::make_unique<QMimeData>();
805
806 for (const auto &type : udmfDataTypes) {
807 if (type == QOhosUdsMeta<::OH_UdsPlainText>::udmfMetaId) {
808 mimeData->setText(dummyText);
809 } else if (type == QOhosUdsMeta<::OH_UdsHtml>::udmfMetaId) {
810 mimeData->setHtml(dummyText);
811 } else if (type == QOhosUdsMeta<::OH_UdsPixelMap>::udmfMetaId) {
812 mimeData->setImageData(QImage(1, 1, QImage::Format::Format_RGBA8888));
813 } else if (type == QOhosUdsMeta<::OH_UdsFileUri>::udmfMetaId
814 || type == QOhosUdsMeta<::OH_UdsHyperlink>::udmfMetaId
815 || isUdmfMetaFileType(type)) {
816 mimeData->setUrls({});
817 }
818 }
819
820 if (mimeData->formats().isEmpty())
821 mimeData->setText(dummyText);
822
823 return mimeData;
824 };
825}
826
828{
829 auto qtAppInfoDataUtdTypeId = tryMapMimeTypeToUtdTypeId(qtAppInfoDataPseudoMimeType);
830 if (!qtAppInfoDataUtdTypeId.has_value()) {
831 qOhosPrintfError("%s: Can't map %s to UTD type id", Q_FUNC_INFO, qtAppInfoDataPseudoMimeType);
832 return false;
833 }
834
835 auto udmfRecords = udmfData.getRecords();
836 auto thisProcessAppInfoData = getAppInfoDataForThisProcess();
837
838 return std::any_of(
839 udmfRecords.begin(), udmfRecords.end(),
840 [&](auto &udmfRecord) {
841 auto optAppInfoData = udmfRecord.tryGetGeneralEntry(qtAppInfoDataUtdTypeId.value());
842 if (!optAppInfoData.has_value())
843 return false;
844
845 auto appInfoData = optAppInfoData.value();
846
847 return
848 static_cast<std::size_t>(appInfoData.size()) == thisProcessAppInfoData.size()
849 && std::memcmp(appInfoData.data(), thisProcessAppInfoData.data(), appInfoData.size()) == 0;
850 });
851}
852
853std::optional<std::string> tryMapUtdTypeIdToMimeType(const std::string &utdTypeId)
854{
855 auto optUtdWithTypeId = utdCreateOrNull(utdTypeId);
856 auto mimeTypes = optUtdWithTypeId
857 ? utdGetMimeTypes(optUtdWithTypeId.get())
858 : std::vector<std::string>();
859
860 return !mimeTypes.empty()
861 ? std::optional(mimeTypes.front())
862 : std::nullopt;
863}
864
865std::optional<std::string> tryMapMimeTypeToUtdTypeId(const std::string &mimeType)
866{
867 // FIXME: the `utdGetTypesByMimeType()` result interpretation is not quite clear.
868 // Documentaion does not mention why there is a list of possible type ids.
869 // Are they ordered in a specific manear? Is it possible to get en ampty list?
870 // Improve and fix the code when these are known.
871 auto utdTypeIds = utdGetTypesByMimeType(mimeType);
872 return
873 !utdTypeIds.empty()
874 ? std::optional(utdTypeIds.front())
875 : std::nullopt;
876}
877
878QT_END_NAMESPACE
bool isEmpty() const
void addEntryToRecord(QOhosUdmfRecord &udmfRecord) const
static UdmfRecordEntryFactory makeForArrayBufferWithMimeType(std::string mimeType, QOhosSupplier< QSpan< const std::uint8_t > > dataSupplier)
static UdmfRecordEntryFactory makeForUdsObjectFactory(QOhosSupplier< QOhosUdsObject< RawUdsObject > > udsObjectFactory)
std::vector< std::uint8_t > getAppInfoDataForThisProcess()
std::vector< T > getMapKeys(const std::map< T, K > &inputMap)
std::function< QOhosUdmfData()> makeUdmfDataFactoryFromQMimeDataImpl(const QMimeData &mimeData, const std::optional< bool > &shareInAppOnly, std::shared_ptr< QMimeData > optLazyProcessingMimeData=nullptr)
UdmfRecordEntryFactory makeUdmfRecordEntryFactoryForUrl(const QUrl &url)
std::vector< std::string > utdGetTypesByMimeType(const std::string &mimeType)
std::optional< T > tryEvalInQtThreadWithConsumer(std::function< void(QOhosConsumer< T >)> qtEvalFunc, ch::nanoseconds timeout)
std::vector< std::string > utdGetMimeTypes(::OH_Utd *utd)
bool hasQMimeDataPeerType(const char *type)
void addMimeDataSuppliersForUrlLikeEntriesFromRecords(std::shared_ptr< void > context, QSpan< const QOhosUdmfRecord > inputRecords, std::map< QString, QOhosSupplier< QVariant > > &outMimeDataSuppliers)
constexpr auto processQMimeDataInQtThreadTimeout
void addDummyEntryToRecord(QOhosUdmfRecord &record)
std::shared_ptr<::OH_UdmfProperty > createUdmfPropertyForUdmfData(::OH_UdmfData *udmfData)
QOhosSupplier< std::optional< T > > makeOptionalValueSupplier(T value)
std::unique_ptr<::OH_UdsArrayBuffer, void(*)(::OH_UdsArrayBuffer *)> createUdsArrayBufferWithData(QSpan< const std::uint8_t > data)
std::vector< UdmfRecordEntryMetaFactory > makeRecordEntryMetaFactoriesForMimeDataFormats(QStringList mimeDataFormats)
void tryProcessEntriesOfTypeInRecords(QSpan< const QOhosUdmfRecord > records, const QOhosConsumer< QOhosUdsObject< T > > &processEntryFunc)
void addMimeDataSuppliersForGeneralEntriesFromRecords(std::shared_ptr< void > context, QSpan< const QOhosUdmfRecord > inputRecords, std::map< QString, QOhosSupplier< QVariant > > &outMimeDataSuppliers)
void addGeneralEntryToRecord(std::string mimeType, QSpan< const std::uint8_t > dataBytes, QOhosUdmfRecord &record)
std::unique_ptr<::OH_UdsArrayBuffer, void(*)(::OH_UdsArrayBuffer *)> createUdsArrayBuffer()
std::function< QOhosUdmfRecord()> tryMakeDefaultUdmfRecordFactoryFromQMimeDataOrNull(const QMimeData &mimeData, std::shared_ptr< QMimeData > optLazyProcessingMimeData)
void addUrlEntryToUdmfRecord(const QUrl &url, QOhosUdmfRecord &outRecord)
bool hasMatchingTypeEntryInRecords(QSpan< const QOhosUdmfRecord > records)
std::shared_ptr<::OH_Utd > utdCreateOrNull(const std::string &typeId)
bool isUdmfMetaFileType(const std::string &type)
std::string const char * mapBoolToTrueFalseStr(bool value)
std::optional< std::string > tryMapMimeTypeToUtdTypeId(const std::string &mimeType)
QT_BEGIN_NAMESPACE std::function< QOhosUdmfData()> makeUdmfDataFactoryFromQMimeData(const QMimeData &mimeData, const std::optional< bool > &shareInAppOnly)
std::function< QOhosUdmfData()> makeLazyProcessingUdmfDataFactoryFromQMimeData(std::shared_ptr< QMimeData > mimeData, const std::optional< bool > &shareInAppOnly)
bool isQOhosUdmfDataConvertedFromThisProcessMimeData(QOhosUdmfData &udmfData)
std::optional< std::string > tryMapUtdTypeIdToMimeType(const std::string &utdTypeId)
QOhosSupplier< std::unique_ptr< QMimeData > > makeLazyFetchingQMimeDataFactoryFromUdmfData(QOhosUdmfData udmfData)
QOhosSupplier< std::unique_ptr< QMimeData > > createQMimeDataFactoryFromUdmfData(QOhosUdmfData udmfData)
QOhosSupplier< std::unique_ptr< QMimeData > > makeDummyQMimeDataFactoryFromUdmfDataTypes(std::vector< std::string > udmfDataTypes)
QOhosSupplier< std::optional< std::string > > optUdmfMetaIdFactory
std::function< UdmfRecordEntryFactory(const QMimeData &)> metaFactoryFunc