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 if (!hasMatchingTypeEntryInRecords<::OH_UdsHyperlink>(inputRecords))
285 return;
286
287 outMimeDataSuppliers.emplace(
288 QString::fromUtf8(mimeTextPlain),
289 [context, inputRecords]() {
290 QStringList urlStrings;
291 tryProcessEntriesOfTypeInRecords<::OH_UdsHyperlink>(
292 inputRecords,
293 [&](auto udsEntry) {
294 urlStrings.append(QString::fromStdString(udsEntry.getContent()));
295 });
296 return QVariant(urlStrings.join(QChar(u'\n')));
297 });
298}
299
301 std::shared_ptr<void> context, QSpan<const QOhosUdmfRecord> inputRecords,
303{
304 auto qtDummyEntryUtdTypeId = tryMapMimeTypeToUtdTypeId(qtDummyEntryPseudoMimeType);
305 if (!qtDummyEntryUtdTypeId.has_value()) {
306 qOhosPrintfWarning("%s: Can't map %s to UTD type id, ignoring", Q_FUNC_INFO, qtDummyEntryPseudoMimeType);
307 }
308
309 for (auto &record : inputRecords) {
310 for (auto &typeId : record.getTypes()) {
311 if (typeId == qtDummyEntryUtdTypeId)
312 continue;
313
314 if (!hasQMimeDataPeerType(typeId.c_str()) && !isUdmfMetaFileType(typeId)) {
315 // FIXME: the `utdGetMimeTypes()` result interpretation is not quite
316 // clear. Documentaion does not mention why there is a list of possible mime types.
317 // Are they ordered in a specific manear? Is it possible to get en ampty list?
318 // Improve and fix the code when these are known.
319 auto optUtdWithTypeId = utdCreateOrNull(typeId);
320 auto mimeTypes = optUtdWithTypeId
321 ? utdGetMimeTypes(optUtdWithTypeId.get())
322 : std::vector<std::string>();
323 if (!mimeTypes.empty()) {
324 outMimeDataSuppliers.emplace(
325 QString::fromStdString(mimeTypes.front()),
326 [context, recordPtr = &record, typeId]() {
327 auto generalEntry = recordPtr->tryGetGeneralEntry(typeId);
328 if (!generalEntry.has_value())
329 return QVariant();
330
331 return QVariant(
332 QByteArray(
333 reinterpret_cast<const char *>(generalEntry.value().data()),
334 generalEntry.value().size()));
335 });
336 } else {
337 qOhosPrintfWarning(
338 "%s: cannot convert UTD type id '%s' to mime type. Skip this entry.",
339 Q_FUNC_INFO, typeId.c_str());
340 }
341 }
342 }
343 }
344}
345
346std::map<QString, QOhosSupplier<QVariant>> makeMimeDataSuppliersMapFromUdmfData(QOhosUdmfData udmfData)
347{
348 struct Context
349 {
350 QOhosUdmfData udmfData;
351 std::vector<QOhosUdmfRecord> records;
352 };
353
354 auto context = QtOhos::makeProxyWithJsThreadDeleter(std::make_shared<Context>());
355 context->udmfData = std::move(udmfData);
356 context->records = context->udmfData.getRecords();
357
358 if (context->records.size() == 0)
359 return {};
360
361 std::map<QString, QOhosSupplier<QVariant>> mimeDataSuppliers;
362
363 auto firstRecordSpan = QSpan(context->records.data(), 1);
364
365 if (hasMatchingTypeEntryInRecords<::OH_UdsPlainText>(firstRecordSpan)) {
366 mimeDataSuppliers.emplace(
367 QString::fromUtf8(mimeTextPlain),
368 [context]() {
369 QVariant value;
370 tryProcessEntriesOfTypeInRecords<::OH_UdsPlainText>(
371 QSpan(context->records.data(), 1),
372 [&](auto udsEntry) {
373 value = QString::fromUtf8(udsEntry.getContent(), -1);
374 });
375 return value;
376 });
377 }
378
379 if (hasMatchingTypeEntryInRecords<::OH_UdsHtml>(firstRecordSpan)) {
380 mimeDataSuppliers.emplace(
381 QString::fromUtf8(mimeTextHtml),
382 [context]() {
383 QVariant value;
384 tryProcessEntriesOfTypeInRecords<::OH_UdsHtml>(
385 QSpan(context->records.data(), 1),
386 [&](auto udsEntry) {
387 value = QString::fromUtf8(udsEntry.getContent(), -1);
388 });
389 return value;
390 });
391 }
392
393 if (hasMatchingTypeEntryInRecords<::OH_UdsPixelMap>(firstRecordSpan)) {
394 mimeDataSuppliers.emplace(
395 QString::fromUtf8(mimeAppXQtImage),
396 [context]() {
397 QVariant value;
398 tryProcessEntriesOfTypeInRecords<::OH_UdsPixelMap>(
399 QSpan(context->records.data(), 1),
400 [&](auto udsEntry) {
401 value = createQImageFromNativePixelMap(udsEntry.getContent().get());
402 });
403 return value;
404 });
405 }
406
407 addMimeDataSuppliersForUrlLikeEntriesFromRecords(
408 context, QSpan(context->records.data(), context->records.size()), mimeDataSuppliers);
409
410 addMimeDataSuppliersForGeneralEntriesFromRecords(context, firstRecordSpan, mimeDataSuppliers);
411
412 return mimeDataSuppliers;
413}
414
416{
417 return url.isLocalFile()
418 ? UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsFileUri>(
419 [url]() {
420 return QOhosUdsObject<::OH_UdsFileUri>(
421 tryMapPathToOhosFileUri(
422 url.toLocalFile().toStdString()).value_or("").c_str());
423 })
424 : UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsHyperlink>(
425 [url]() {
426 return QOhosUdsObject<::OH_UdsHyperlink>(
427 url.toString().toStdString().c_str());
428 });
429}
430
431void addUrlEntryToUdmfRecord(const QUrl &url, QOhosUdmfRecord &outRecord)
432{
433 makeUdmfRecordEntryFactoryForUrl(url).addEntryToRecord(outRecord);
434}
435
437{
438 return {
439 QArkUi::callArkUiOrFailOnNullResult(
440 Q_OHOS_NAMED_FUNC(::OH_UdmfProperty_Create),
441 udmfData),
442 [](::OH_UdmfProperty *udmfProperty) {
443 QArkUi::callArkUi(
444 Q_OHOS_NAMED_FUNC(::OH_UdmfProperty_Destroy),
445 udmfProperty);
446 }
447 };
448}
449
451{
452 return {
453 QArkUi::callArkUiOrFailOnNullResult(
454 Q_OHOS_NAMED_FUNC(::OH_UdsArrayBuffer_Create)),
455 [](::OH_UdsArrayBuffer *udsArrayBuffer) {
456 QArkUi::callArkUiOrFailOnErrorResult(
457 Q_OHOS_NAMED_FUNC(::OH_UdsArrayBuffer_Destroy),
458 udsArrayBuffer);
459 }
460 };
461}
462
464 createUdsArrayBufferWithData(QSpan<const std::uint8_t> data)
465{
466 auto udsArrayBuffer = createUdsArrayBuffer();
467 QArkUi::callArkUiOrFailOnErrorResult(
468 Q_OHOS_NAMED_FUNC(::OH_UdsArrayBuffer_SetData),
469 udsArrayBuffer.get(), const_cast<std::uint8_t *>(data.data()), data.size());
470 return udsArrayBuffer;
471}
472
473void addGeneralEntryToRecord(std::string mimeType, QSpan<const std::uint8_t> dataBytes, QOhosUdmfRecord &record)
474{
475 if (dataBytes.size() == 0) {
476 qOhosPrintfWarning(
477 "%s: got empty data for type '%s', which is not supported by UDMF record, skipping this entry",
478 Q_FUNC_INFO, mimeType.c_str());
479 return;
480 }
481
482 auto optUtdTypeId = tryMapMimeTypeToUtdTypeId(mimeType);
483 if (optUtdTypeId.has_value()) {
484 record.addGeneralEntry(optUtdTypeId.value(), dataBytes);
485 } else {
486 qOhosPrintfWarning(
487 "%s: cannot convert mime type '%s' to type id. Skip this entry.",
488 Q_FUNC_INFO, mimeType.c_str());
489 }
490}
491
493{
494 const std::uint8_t dummyEntryDataByte = 0;
495 addGeneralEntryToRecord(
496 qtDummyEntryPseudoMimeType, QSpan(&dummyEntryDataByte, 1), record);
497}
498
499template<typename RawUdsObject>
500UdmfRecordEntryFactory UdmfRecordEntryFactory::makeForUdsObjectFactory(
501 QOhosSupplier<QOhosUdsObject<RawUdsObject>> udsObjectFactory)
502{
503 auto sharedUdsObjectFactory = QtOhos::moveToSharedPtr(std::move(udsObjectFactory));
504 return UdmfRecordEntryFactory(
505 [sharedUdsObjectFactory]() {
506 void *data = (*sharedUdsObjectFactory)().release();
507 qOhosPrintfWarning(
508 "%s: generated '%s' data for OH_UdmfRecordProvider: %p",
509 Q_FUNC_INFO, QOhosUdsMeta<RawUdsObject>::udmfMetaId, data);
510 return data;
511 },
512 [sharedUdsObjectFactory](QOhosUdmfRecord &record) {
513 record.addEntry((*sharedUdsObjectFactory)());
514 });
515}
516
517UdmfRecordEntryFactory UdmfRecordEntryFactory::makeForArrayBufferWithMimeType(
518 std::string mimeType, QOhosSupplier<QSpan<const std::uint8_t>> dataSupplier)
519{
520 auto sharedDataSupplier = QtOhos::moveToSharedPtr(std::move(dataSupplier));
521 return UdmfRecordEntryFactory(
522 [mimeType, sharedDataSupplier]() {
523 auto dataSpan = (*sharedDataSupplier)();
524 auto udsArrayBuffer = createUdsArrayBufferWithData(dataSpan);
525 qOhosPrintfDebug(
526 "%s: generated array buffer for OH_UdmfRecordProvider, mimeType='%s', size=%zu",
527 Q_FUNC_INFO, mimeType.c_str(), static_cast<std::size_t>(dataSpan.size()));
528 return udsArrayBuffer.release();
529 },
530 [mimeType, sharedDataSupplier](QOhosUdmfRecord &record) {
531 addGeneralEntryToRecord(mimeType, (*sharedDataSupplier)(), record);
532 });
533}
534
536{
537 return UdmfRecordEntryFactory(
538 []() {
539 return nullptr;
540 },
541 [](QOhosUdmfRecord &) {
542 });
543}
544
545UdmfRecordEntryFactory::UdmfRecordEntryFactory(
546 QOhosSupplier<void *> recordProviderDataFactory,
547 std::function<void(QOhosUdmfRecord &)> recordAddEntryFunc)
548 : m_recordProviderDataFactory(std::move(recordProviderDataFactory))
549 , m_recordAddEntryFunc(std::move(recordAddEntryFunc))
550{
551}
552
554{
555 m_recordAddEntryFunc(udmfRecord);
556}
557
559{
560 return m_recordProviderDataFactory();
561}
562
564{
565 std::vector<UdmfRecordEntryMetaFactory> recordEntryMetaFactories;
566 for (auto qStrFormat : mimeDataFormats) {
567 auto format = qStrFormat.toStdString();
568 if (format == mimeTextPlain) {
569 recordEntryMetaFactories.emplace_back(
570 UdmfRecordEntryMetaFactory{
571 makeOptionalValueSupplier<std::string>(QOhosUdsMeta<::OH_UdsPlainText>::udmfMetaId),
572 [](const QMimeData &mimeData) {
573 auto dataText = mimeData.text();
574 return UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsPlainText>(
575 [dataText]() {
576 return QOhosUdsObject<::OH_UdsPlainText>(dataText.toStdString().c_str());
577 });
578 }
579 });
580 } else if (format == mimeTextHtml) {
581 recordEntryMetaFactories.emplace_back(
582 UdmfRecordEntryMetaFactory{
583 makeOptionalValueSupplier<std::string>(QOhosUdsMeta<::OH_UdsHtml>::udmfMetaId),
584 [](const QMimeData &mimeData) {
585 auto dataHtml = mimeData.html();
586 return UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsHtml>(
587 [dataHtml]() {
588 return QOhosUdsObject<::OH_UdsHtml>(dataHtml.toStdString().c_str());
589 });
590 }
591 });
592 } else if (format == mimeTextUriList) {
593 recordEntryMetaFactories.emplace_back(
594 UdmfRecordEntryMetaFactory{
595 {},
596 [](const QMimeData &mimeData) {
597 auto dataUrls = mimeData.urls();
598 return !dataUrls.isEmpty()
599 ? makeUdmfRecordEntryFactoryForUrl(dataUrls.first())
600 : UdmfRecordEntryFactory::makeDummy();
601 }
602 });
603 } else if (format == mimeAppXQtImage) {
604 recordEntryMetaFactories.emplace_back(
605 UdmfRecordEntryMetaFactory{
606 makeOptionalValueSupplier<std::string>(QOhosUdsMeta<::OH_UdsPixelMap>::udmfMetaId),
607 [](const QMimeData &mimeData) {
608 auto dataImage = qvariant_cast<QImage>(mimeData.imageData());
609 return UdmfRecordEntryFactory::makeForUdsObjectFactory<::OH_UdsPixelMap>(
610 [dataImage]() {
611 return QOhosUdsObject<::OH_UdsPixelMap>(makeOhosNativePixelMapFromQImage(dataImage));
612 });
613 }
614 });
615 } else {
616 recordEntryMetaFactories.emplace_back(
617 UdmfRecordEntryMetaFactory{
618 [qStrFormat]() {
619 return tryMapMimeTypeToUtdTypeId(qStrFormat.toStdString());
620 },
621 [qStrFormat](const QMimeData &mimeData) {
622 auto dataBytes = mimeData.data(qStrFormat);
623 return UdmfRecordEntryFactory::makeForArrayBufferWithMimeType(
624 qStrFormat.toStdString(),
625 [dataBytes]() {
626 return QSpan<const std::uint8_t>(
627 reinterpret_cast<const std::uint8_t *>(dataBytes.constData()),
628 dataBytes.length());
629 });
630 }
631 });
632 }
633 }
634
635 return recordEntryMetaFactories;
636}
637
639 const QMimeData &mimeData, std::shared_ptr<QMimeData> optLazyProcessingMimeData)
640{
641 auto recordEntryMetaFactories = makeRecordEntryMetaFactoriesForMimeDataFormats(mimeData.formats());
642 if (recordEntryMetaFactories.empty())
643 return nullptr;
644
645 struct Context
646 {
647 std::vector<UdmfRecordEntryFactory> directEntryFactories;
648 std::vector<UdmfRecordEntryMetaFactory> providerEntries;
649 };
650
651 auto context = std::make_shared<Context>();
652
653 const bool useRecordProvider = optLazyProcessingMimeData != nullptr;
654
655 for (auto &recordEntryFactoryEntry : recordEntryMetaFactories) {
656 if (useRecordProvider && recordEntryFactoryEntry.optUdmfMetaIdFactory) {
657 context->providerEntries.push_back(std::move(recordEntryFactoryEntry));
658 } else {
659 context->directEntryFactories.push_back(
660 recordEntryFactoryEntry.metaFactoryFunc(mimeData));
661 }
662 }
663
664 return [context, optLazyProcessingMimeData]() {
665 QOhosUdmfRecord record;
666 for (const auto &directEntryFactory : context->directEntryFactories)
667 directEntryFactory.addEntryToRecord(record);
668
669 auto entryMetaFactoryFuncsForProvider =
670 std::make_shared<std::map<std::string, std::function<UdmfRecordEntryFactory(const QMimeData &)>>>();
671 for (const auto &providerEntry : context->providerEntries) {
672 auto optUdmfMetaId = providerEntry.optUdmfMetaIdFactory();
673 if (optUdmfMetaId.has_value()) {
674 entryMetaFactoryFuncsForProvider->emplace(
675 optUdmfMetaId.value(), providerEntry.metaFactoryFunc);
676 }
677 }
678
679 if (!entryMetaFactoryFuncsForProvider->empty()) {
680 if (record.isEmpty())
682
683 auto lazyProcessingMimeData = optLazyProcessingMimeData;
684 record.setProviderForDataFetchFunc(
685 getMapKeys(*entryMetaFactoryFuncsForProvider),
686 [entryMetaFactoryFuncsForProvider, lazyProcessingMimeData](const char *type) {
687 qOhosPrintfDebug("%s: got request for data '%s'", Q_FUNC_INFO, type);
688
689 auto typeStr = std::string(type);
690 auto optUdmfRecordEntryFactory = tryEvalInQtThreadWithConsumer<UdmfRecordEntryFactory>(
691 [entryMetaFactoryFuncsForProvider, lazyProcessingMimeData, typeStr](QOhosConsumer<UdmfRecordEntryFactory> resultConsumer) {
692 qOhosPrintfDebug("%s: processing request for data '%s' in Qt thread", Q_FUNC_INFO, typeStr.c_str());
693 auto entryMetaFactoryFuncIter = entryMetaFactoryFuncsForProvider->find(typeStr);
694 resultConsumer(
695 entryMetaFactoryFuncIter != entryMetaFactoryFuncsForProvider->end()
696 ? entryMetaFactoryFuncIter->second(*lazyProcessingMimeData)
698 qOhosPrintfDebug("%s: finished processing in Qt thread", Q_FUNC_INFO);
699 },
700 processQMimeDataInQtThreadTimeout);
701 qOhosPrintfDebug(
702 "%s: got result from Qt thread: %s",
703 Q_FUNC_INFO, QtOhos::mapBoolToTrueFalseStr(optUdmfRecordEntryFactory.has_value()));
704
705 void *providerResult = optUdmfRecordEntryFactory.has_value()
706 ? optUdmfRecordEntryFactory.value().makeRecordProviderDataForEntry()
707 : nullptr;
708 qOhosPrintfDebug("%s: provider result: %p", Q_FUNC_INFO, providerResult);
709
710 return providerResult;
711 });
712 }
713 return record;
714 };
715}
716
718 const QMimeData &mimeData, const std::optional<bool> &shareInAppOnly,
719 std::shared_ptr<QMimeData> optLazyProcessingMimeData = nullptr)
720{
721 std::vector<std::function<QOhosUdmfRecord()>> recordFactories;
722
723 auto optDefaultRecordFactory = tryMakeDefaultUdmfRecordFactoryFromQMimeDataOrNull(
724 mimeData, optLazyProcessingMimeData);
725 if (optDefaultRecordFactory)
726 recordFactories.push_back(std::move(optDefaultRecordFactory));
727
728 if (mimeData.hasUrls()) {
729 auto urls = mimeData.urls();
730 for (int i = 1; i < urls.size(); ++i) {
731 auto dataUrl = urls.at(i);
732 recordFactories.emplace_back(
733 [dataUrl]() {
734 QOhosUdmfRecord record;
735 addUrlEntryToUdmfRecord(dataUrl, record);
736 return record;
737 });
738 }
739 }
740
741 recordFactories.emplace_back(
742 []() {
743 QOhosUdmfRecord record;
744 auto appInfoDataBytes = getAppInfoDataForThisProcess();
745 addGeneralEntryToRecord(
746 qtAppInfoDataPseudoMimeType, QSpan<const std::uint8_t>(appInfoDataBytes.data(), appInfoDataBytes.size()), record);
747 return record;
748 });
749
750 return [shareInAppOnly, recordFactories = std::move(recordFactories)]() {
751 QOhosUdmfData udmfData;
752
753 if (shareInAppOnly.has_value()) {
754 auto udmfShareProperty = createUdmfPropertyForUdmfData(udmfData.nativePtr());
755 QArkUi::callArkUiOrFailOnErrorResult(
756 Q_OHOS_NAMED_FUNC(::OH_UdmfProperty_SetShareOption),
757 udmfShareProperty.get(),
758 shareInAppOnly.value()
759 ? ::Udmf_ShareOption::SHARE_OPTIONS_IN_APP
760 : ::Udmf_ShareOption::SHARE_OPTIONS_CROSS_APP);
761 }
762
763 for (const auto &recordFactory : recordFactories)
764 udmfData.addRecord(recordFactory());
765
766 return udmfData;
767 };
768}
769
770}
771
773 const QMimeData &mimeData, const std::optional<bool> &shareInAppOnly)
774{
775 return makeUdmfDataFactoryFromQMimeDataImpl(mimeData, shareInAppOnly, nullptr);
776}
777
779 std::shared_ptr<QMimeData> mimeData, const std::optional<bool> &shareInAppOnly)
780{
781 return makeUdmfDataFactoryFromQMimeDataImpl(*mimeData, shareInAppOnly, mimeData);
782}
783
785{
786 std::map<QString, QVariant> mimeDataMap;
787 for (auto &supplierEntry : makeMimeDataSuppliersMapFromUdmfData(std::move(udmfData)))
788 mimeDataMap.emplace(supplierEntry.first, supplierEntry.second());
789
790 return makeQOhosMimeDataFactory(std::move(mimeDataMap));
791}
792
794{
795 std::map<QString, QOhosSupplier<QVariant>> threadSafeSuppliers;
796 for (auto &supplierEntry : makeMimeDataSuppliersMapFromUdmfData(std::move(udmfData))) {
797 threadSafeSuppliers.emplace(
798 supplierEntry.first,
799 [mimeType = supplierEntry.first.toStdString(), baseSupplier = std::move(supplierEntry.second)]() {
800 qOhosPrintfDebug("%s: fetching data for MIME type '%s'", Q_FUNC_INFO, mimeType.c_str());
801 return QtOhos::evalInJsThread(
802 [&](QtOhos::JsState &) {
803 return baseSupplier();
804 },
805 Q_FUNC_INFO);
806 });
807 }
808
809 return makeQOhosLazyFetchMimeDataFactory(std::move(threadSafeSuppliers));
810}
811
813 std::vector<std::string> udmfDataTypes)
814{
815 return [udmfDataTypes = std::move(udmfDataTypes)]() {
816 const auto dummyText = QString::fromUtf8(
817 "OHOS QPA: Drag data is not available before DROP. This is a system limitation.");
818
819 auto mimeData = std::make_unique<QMimeData>();
820
821 for (const auto &type : udmfDataTypes) {
822 if (type == QOhosUdsMeta<::OH_UdsPlainText>::udmfMetaId) {
823 mimeData->setText(dummyText);
824 } else if (type == QOhosUdsMeta<::OH_UdsHtml>::udmfMetaId) {
825 mimeData->setHtml(dummyText);
826 } else if (type == QOhosUdsMeta<::OH_UdsPixelMap>::udmfMetaId) {
827 mimeData->setImageData(QImage(1, 1, QImage::Format::Format_RGBA8888));
828 } else if (type == QOhosUdsMeta<::OH_UdsFileUri>::udmfMetaId || isUdmfMetaFileType(type)) {
829 mimeData->setUrls({});
830 } else if (type == QOhosUdsMeta<::OH_UdsHyperlink>::udmfMetaId) {
831 mimeData->setUrls({});
832 mimeData->setText(dummyText);
833 }
834 }
835
836 if (mimeData->formats().isEmpty())
837 mimeData->setText(dummyText);
838
839 return mimeData;
840 };
841}
842
844{
845 auto qtAppInfoDataUtdTypeId = tryMapMimeTypeToUtdTypeId(qtAppInfoDataPseudoMimeType);
846 if (!qtAppInfoDataUtdTypeId.has_value()) {
847 qOhosPrintfError("%s: Can't map %s to UTD type id", Q_FUNC_INFO, qtAppInfoDataPseudoMimeType);
848 return false;
849 }
850
851 auto udmfRecords = udmfData.getRecords();
852 auto thisProcessAppInfoData = getAppInfoDataForThisProcess();
853
854 return std::any_of(
855 udmfRecords.begin(), udmfRecords.end(),
856 [&](auto &udmfRecord) {
857 auto optAppInfoData = udmfRecord.tryGetGeneralEntry(qtAppInfoDataUtdTypeId.value());
858 if (!optAppInfoData.has_value())
859 return false;
860
861 auto appInfoData = optAppInfoData.value();
862
863 return
864 static_cast<std::size_t>(appInfoData.size()) == thisProcessAppInfoData.size()
865 && std::memcmp(appInfoData.data(), thisProcessAppInfoData.data(), appInfoData.size()) == 0;
866 });
867}
868
869std::optional<std::string> tryMapUtdTypeIdToMimeType(const std::string &utdTypeId)
870{
871 auto optUtdWithTypeId = utdCreateOrNull(utdTypeId);
872 auto mimeTypes = optUtdWithTypeId
873 ? utdGetMimeTypes(optUtdWithTypeId.get())
874 : std::vector<std::string>();
875
876 return !mimeTypes.empty()
877 ? std::optional(mimeTypes.front())
878 : std::nullopt;
879}
880
881std::optional<std::string> tryMapMimeTypeToUtdTypeId(const std::string &mimeType)
882{
883 // FIXME: the `utdGetTypesByMimeType()` result interpretation is not quite clear.
884 // Documentaion does not mention why there is a list of possible type ids.
885 // Are they ordered in a specific manear? Is it possible to get en ampty list?
886 // Improve and fix the code when these are known.
887 auto utdTypeIds = utdGetTypesByMimeType(mimeType);
888 return
889 !utdTypeIds.empty()
890 ? std::optional(utdTypeIds.front())
891 : std::nullopt;
892}
893
894QT_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