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