8#include <QtCore/qcoreapplication.h>
9#include <QtCore/qjnienvironment.h>
10#include <QtCore/qjniobject.h>
11#include <QtCore/qurl.h>
12#include <QtCore/qdatetime.h>
13#include <QtCore/qmimedatabase.h>
14#include <QtCore/qdebug.h>
15#include <QtCore/qloggingcategory.h>
19using namespace QNativeInterface;
20using namespace Qt::StringLiterals;
24Q_DECLARE_JNI_CLASS(ParcelFileDescriptorType,
"android/os/ParcelFileDescriptor");
37 static QJniObject contentResolver;
38 if (!contentResolver.isValid()) {
39 contentResolver = QJniObject(QNativeInterface::QAndroidApplication::context())
40 .callMethod<QtJniTypes::ContentResolver>(
"getContentResolver");
43 return contentResolver;
50 setFileName(filename);
54 std::optional<QFile::Permissions> permissions)
56 Q_UNUSED(permissions);
58 if (openMode == QIODeviceBase::NotOpen)
61 if (!m_documentFile->exists() && (openMode & QIODevice::WriteOnly)) {
63 DocumentFilePtr parent = m_documentFile->parent();
65 qCWarning(lcAndroidContentFileEngine) <<
"Cannot create file under a null parent.";
69 if (!parent->exists() || !parent->isDirectory()) {
70 qCWarning(lcAndroidContentFileEngine)
71 <<
"Cannot create file, parent doesn't exist or not a directory:"
72 << parent->uri().toString();
76 const QString fileName = m_documentFile->initialName();
77 if (fileName.isEmpty()) {
78 qCWarning(lcAndroidContentFileEngine) <<
"Coudln't determine filename from content URI:"
79 << m_documentFile->uri().toString();
84 QString mimeType = db.mimeTypeForFile(fileName, QMimeDatabase::MatchDefault).name();
86 m_documentFile = parent->createFile(mimeType, fileName);
87 if (!m_documentFile) {
88 qCWarning(lcAndroidContentFileEngine) <<
"Failed to create new document under parent:"
89 << parent->uri().toString();
94 using namespace QtJniTypes;
95 const QString openModeStr = (openMode & QIODevice::WriteOnly) ?
"w"_L1 :
"r"_L1;
96 m_pfd = QtContentFileEngine::callStaticMethod<ParcelFileDescriptorType>(
98 contentResolverInstance().object<ContentResolver>(),
99 m_documentFile->uri().object<Uri>(),
102 if (!m_pfd.isValid())
105 const auto fd = m_pfd.callMethod<jint>(
"getFd");
108 closeNativeFileDescriptor();
112 return QFSFileEngine::open(openMode, fd, QFile::DontCloseHandle);
117 closeNativeFileDescriptor();
118 return QFSFileEngine::close();
123 if (m_pfd.isValid()) {
124 m_pfd.callMethod<
void>(
"close");
125 m_pfd = QJniObject();
131 return m_documentFile->length();
136 return m_documentFile->remove();
141 if (m_documentFile->rename(newName)) {
142 m_initialFile = newName;
149 std::optional<QFileDevice::Permissions> permissions)
const
151 Q_UNUSED(permissions)
153 QString tmp = dirName;
154 tmp.remove(m_initialFile);
156 QStringList dirParts = tmp.split(u'/');
157 dirParts.removeAll(
"");
159 if (dirParts.isEmpty())
162 auto createdDir = m_documentFile;
163 bool allDirsCreated =
true;
164 for (
const auto &dir : dirParts) {
166 bool subDirExists =
false;
167 for (
const DocumentFilePtr &subDir : m_documentFile->listFiles()) {
168 if (dir == subDir->initialName() && subDir->isDirectory()) {
175 createdDir = createdDir->createDirectory(dir);
177 allDirsCreated =
false;
182 if (!createParentDirectories)
186 return allDirsCreated;
191 Q_UNUSED(recurseParentDirectories)
193 return DocumentFile::parseFromAnyUri(dirName)->remove();
198 return m_documentFile->id().toUtf8();
204 case QFile::FileModificationTime:
205 return m_documentFile->lastModified();
217 if (!m_documentFile->exists())
221 if (!m_documentFile->canRead())
224 flags |= ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm;
226 if (m_documentFile->isDirectory()) {
227 flags |= DirectoryType;
230 if (m_documentFile->canWrite())
231 flags |= WriteOwnerPerm|WriteUserPerm|WriteGroupPerm|WriteOtherPerm;
240 case AbsolutePathName:
241 case CanonicalPathName:
245 return m_documentFile->uri().toString();
247 const QString queriedName = m_documentFile->name();
248 return queriedName.isEmpty() ? m_documentFile->initialName() : queriedName;
259 const QStringList &filterNames)
261 return std::make_unique<AndroidContentFileEngineIterator>(path, filters, filterNames);
270 if (fileName.startsWith(contentScheme))
271 return std::make_unique<AndroidContentFileEngine>(fileName);
278 const QString &path, QDirListing::IteratorFlags filters,
const QStringList &filterNames)
289 if (m_index == -1 && m_files.isEmpty()) {
290 const auto currentPath = path();
291 if (currentPath.isEmpty())
294 const auto iterDoc = DocumentFile::parseFromAnyUri(currentPath);
295 if (iterDoc->isDirectory())
296 for (
const auto &doc : iterDoc->listFiles())
298 if (m_files.isEmpty())
304 if (m_index < m_files.size() - 1) {
314 if (m_index < 0 || m_index >= m_files.size())
316 return m_files.at(m_index)->name();
321 if (m_index < 0 || m_index >= m_files.size())
323 return m_files.at(m_index)->uri().toString();
331 explicit Cursor(
const QJniObject &object)
336 if (m_object.isValid())
337 m_object.callMethod<
void>(
"close");
350 int type = m_object.callMethod<jint>(
"getType", columnIndex);
354 case FIELD_TYPE_INTEGER:
355 return QVariant::fromValue(m_object.callMethod<jlong>(
"getLong", columnIndex));
356 case FIELD_TYPE_FLOAT:
357 return QVariant::fromValue(m_object.callMethod<jdouble>(
"getDouble", columnIndex));
358 case FIELD_TYPE_STRING:
359 return QVariant::fromValue(m_object.callMethod<jstring>(
"getString",
360 columnIndex).toString());
361 case FIELD_TYPE_BLOB:
362 return QVariant::fromValue(m_object.callMethod<QByteArray>(
"getBlob", columnIndex));
368 const QStringList &projection = {},
369 const QString &selection = {},
370 const QStringList &selectionArgs = {},
371 const QString &sortOrder = {})
373 using namespace QtJniTypes;
374 auto cursor = QtContentFileEngine::callStaticMethod<CursorType>(
"query",
375 contentResolverInstance().object<ContentResolver>(),
377 QJniArray(projection),
378 selection.isEmpty() ?
nullptr : selection,
379 QJniArray(selectionArgs),
380 sortOrder.isEmpty() ?
nullptr : sortOrder);
381 if (!cursor.isValid())
383 return std::make_unique<Cursor>(cursor);
388 const auto query = queryUri(uri, {column});
392 if (query->rowCount() != 1 || query->columnCount() != 1)
394 query->moveToFirst();
395 return query->data(0);
400 return m_object.callMethod<jboolean>(
"isNull", columnIndex);
403 int columnCount()
const {
return m_object.callMethod<jint>(
"getColumnCount"); }
404 int rowCount()
const {
return m_object.callMethod<jint>(
"getCount"); }
405 int row()
const {
return m_object.callMethod<jint>(
"getPosition"); }
406 bool isFirst()
const {
return m_object.callMethod<jboolean>(
"isFirst"); }
407 bool isLast()
const {
return m_object.callMethod<jboolean>(
"isLast"); }
408 bool moveToFirst() {
return m_object.callMethod<jboolean>(
"moveToFirst"); }
409 bool moveToLast() {
return m_object.callMethod<jboolean>(
"moveToLast"); }
410 bool moveToNext() {
return m_object.callMethod<jboolean>(
"moveToNext"); }
423
424
425
426
427
428
429
430
454 return QJniObject::callStaticMethod<jstring, QtJniTypes::Uri>(
455 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
457 uri.object()).toString();
462 return QJniObject::callStaticMethod<jstring, QtJniTypes::Uri>(
463 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
465 uri.object()).toString();
470 return QJniObject::callStaticMethod<QtJniTypes::Uri>(
471 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
472 "buildChildDocumentsUriUsingTree",
473 uri.object<QtJniTypes::Uri>(),
474 QJniObject::fromString(parentDocumentId).object<jstring>());
480 return QJniObject::callStaticMethod<QtJniTypes::Uri>(
481 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
482 "buildDocumentUriUsingTree",
483 treeUri.object<QtJniTypes::Uri>(),
484 QJniObject::fromString(documentId).object<jstring>());
489 return QJniObject::callStaticMethod<jboolean>(
490 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
492 QNativeInterface::QAndroidApplication::context(),
493 uri.object<QtJniTypes::Uri>());
498 return QJniObject::callStaticMethod<jboolean>(
499 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
501 uri.object<QtJniTypes::Uri>());
505 const QString &displayName)
507 return QJniObject::callStaticMethod<QtJniTypes::Uri>(
508 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
510 contentResolverInstance().object<QtJniTypes::ContentResolver>(),
511 parentDocumentUri.object<QtJniTypes::Uri>(),
512 QJniObject::fromString(mimeType).object<jstring>(),
513 QJniObject::fromString(displayName).object<jstring>());
518 const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
522 return QJniObject::callStaticMethod<jboolean>(
523 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
525 contentResolverInstance().object<QtJniTypes::ContentResolver>(),
526 documentUri.object<QtJniTypes::Uri>());
530 const QJniObject &sourceParentDocumentUri,
531 const QJniObject &targetParentDocumentUri)
533 const int flags = Cursor::queryColumn(sourceDocumentUri, Document::COLUMN_FLAGS).toInt();
537 return QJniObject::callStaticMethod<QtJniTypes::Uri>(
538 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
540 contentResolverInstance().object<QtJniTypes::ContentResolver>(),
541 sourceDocumentUri.object<QtJniTypes::Uri>(),
542 sourceParentDocumentUri.object<QtJniTypes::Uri>(),
543 targetParentDocumentUri.object<QtJniTypes::Uri>());
548 const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
552 return QJniObject::callStaticMethod<QtJniTypes::Uri>(
553 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
555 contentResolverInstance().object<QtJniTypes::ContentResolver>(),
556 documentUri.object<QtJniTypes::Uri>(),
557 QJniObject::fromString(displayName).object<jstring>());
569 MakeableDocumentFile(
const QJniObject &uri,
const DocumentFilePtr &parent = {})
572 QString uriString = uri.toString();
577 uriString.chop(std::size(childrenSegment) - 1);
580 const QString path = QUrl(uriString).path();
581 if (path.isEmpty() || path == u"/")
584 int displayNameStartIndex = uriString.lastIndexOf(u'/') + 1;
586 const int encodedSlashPos = uriString.lastIndexOf(u"%2F");
587 if (encodedSlashPos != -1)
588 displayNameStartIndex = qMax(displayNameStartIndex, encodedSlashPos + 3);
590 const int encodedColonPos = uriString.lastIndexOf(u"%3A");
591 if (encodedColonPos != -1)
592 displayNameStartIndex = qMax(displayNameStartIndex, encodedColonPos + 3);
594 m_displayName = uriString.mid(displayNameStartIndex);
597 MakeableDocumentFile(
const QJniObject &uri,
const QString &displayName,
const DocumentFilePtr &parent = {})
604 const QString &displayName,
605 const DocumentFilePtr &parent)
616 const auto jSegments = uri.callMethod<QtJniTypes::List>(
"getPathSegments");
617 if (!jSegments.isValid())
620 QStringList segments;
621 for (
int i = 0; i < jSegments.callMethod<jint>(
"size"); ++i)
622 segments.append(jSegments.callMethod<QJniObject>(
"get", i).toString());
629 return QJniObject::callStaticMethod<QtJniTypes::Uri>(
630 QtJniTypes::Traits<QtJniTypes::Uri>::className(),
632 QJniObject::fromString(uri).object<jstring>());
637 const QJniObject uri = parseUri(fileName);
638 if (!uri.isValid() || uri.toString().isEmpty())
641 const auto scheme = uri.callMethod<QString>(
"getScheme");
642 if (scheme != QLatin1String(contentScheme))
643 return std::make_shared<MakeableDocumentFile>(uri);
645 const auto authority = uri.callMethod<QString>(
"getAuthority");
646 const QStringList segments = getPathSegments(uri);
648 const int treeIndex = segments.indexOf(
treeSegment);
651 DocumentFilePtr parent;
652 QJniObject parsedUri;
654 if (treeIndex != -1) {
656 if (segments.size() <= treeIndex + 1)
657 return fromSingleUri(uri);
659 const QString treeId = segments.at(treeIndex + 1);
660 const QString encodedTreeId = QUrl::toPercentEncoding(treeId);
661 const QString treeUriString =
"%1://%2/%3/%4"_L1.arg(scheme, authority,
662 treeSegment, encodedTreeId);
663 const QJniObject treeUri = parseUri(treeUriString);
665 const int midIndex = (docIndex > treeIndex) ? docIndex + 1 : treeIndex + 2;
666 QString docIdOrPath = segments.mid(midIndex).join(
'/');
668 if (docIdOrPath.isEmpty())
669 return fromTreeUri(treeUri);
672 if (docIndex > treeIndex)
673 fullDocId = docIdOrPath;
675 fullDocId = treeId + u'/' + docIdOrPath;
677 parsedUri = buildDocumentUriUsingTree(treeUri, fullDocId);
679 const int lastSlash = fullDocId.lastIndexOf(
'/');
680 if (lastSlash != -1) {
681 const QString parentDocId = fullDocId.left(lastSlash);
683 if (!parentDocId.isEmpty() && parentDocId.startsWith(treeId)) {
684 QJniObject parentUri = buildDocumentUriUsingTree(treeUri, parentDocId);
685 parent = std::make_shared<MakeableDocumentFile>(parentUri);
690 parent = fromTreeUri(treeUri);
691 }
else if (docIndex != -1) {
692 const QString docId = segments.mid(docIndex + 1).join(
'/');
693 const QString encodedDocId = QUrl::toPercentEncoding(docId);
694 const QString docUriStr =
"%1://%2/%3/%4"_L1.arg(scheme, authority,
695 documentSegment, encodedDocId);
696 parsedUri = parseUri(docUriStr);
698 const int lastSlash = docId.lastIndexOf(
'/');
699 if (lastSlash != -1) {
700 const QString parentDocId = docId.left(lastSlash);
701 const QString encodedParentDocId = QUrl::toPercentEncoding(parentDocId);
702 const QString parentUriStr =
"%1://%2/%3/%4"_L1.arg(scheme, authority, documentSegment,
704 parent = fromSingleUri(parseUri(parentUriStr));
708 if (segments.size() > 1) {
709 QStringList parentSegments = segments;
710 parentSegments.removeLast();
711 const QString parendDocId = parentSegments.join(
'/');
712 const QString parentUriStr =
"%1://%2/%3"_L1.arg(scheme, authority, parendDocId);
713 parent = fromSingleUri(parseUri(parentUriStr));
717 if (!parsedUri.isValid())
718 return fromSingleUri(uri);
720 auto docFile = std::make_shared<MakeableDocumentFile>(parsedUri);
722 docFile->m_parent = parent;
732 return std::make_shared<MakeableDocumentFile>(uri);
737 if (!treeUri.isValid())
740 if (isDocumentUri(treeUri))
741 return std::make_shared<MakeableDocumentFile>(treeUri);
743 if (isTreeUri(treeUri)) {
744 const QString docId = treeDocumentId(treeUri);
745 if (!docId.isEmpty()) {
746 const QJniObject docUri = buildDocumentUriUsingTree(treeUri, docId);
747 return std::make_shared<MakeableDocumentFile>(docUri);
751 return std::make_shared<MakeableDocumentFile>(treeUri);
757 const QString decodedName = QUrl::fromPercentEncoding(displayName.toUtf8());
758 return std::make_shared<MakeableDocumentFile>(
759 createDocument(m_uri, mimeType, decodedName),
768 return std::make_shared<MakeableDocumentFile>(
769 createDocument(m_uri, Document::MIME_TYPE_DIR, displayName),
787 return m_displayName;
792 return Cursor::queryColumn(m_uri, Document::COLUMN_DISPLAY_NAME).toString();
797 return DocumentsContract::documentId(uri());
802 return Cursor::queryColumn(m_uri, Document::COLUMN_MIME_TYPE).toString();
807 return mimeType() == Document::MIME_TYPE_DIR;
812 const QString type = mimeType();
813 return type != Document::MIME_TYPE_DIR && !type.isEmpty();
818 return isDocumentUri(m_uri) && (Cursor::queryColumn(m_uri,
819 Document::COLUMN_FLAGS).toInt() & Document::FLAG_VIRTUAL_DOCUMENT);
824 const auto timeVariant = Cursor::queryColumn(m_uri, Document::COLUMN_LAST_MODIFIED);
825 if (timeVariant.isValid())
826 return QDateTime::fromMSecsSinceEpoch(timeVariant.toLongLong());
832 return Cursor::queryColumn(m_uri, Document::COLUMN_SIZE).toLongLong();
836constexpr int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001;
837constexpr int FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002;
842 const auto context = QJniObject(QNativeInterface::QAndroidApplication::context());
843 const bool selfUriPermission = context.callMethod<jint>(
"checkCallingOrSelfUriPermission",
844 m_uri.object<QtJniTypes::Uri>(),
845 FLAG_GRANT_READ_URI_PERMISSION);
846 if (selfUriPermission != 0)
849 return !mimeType().isEmpty();
854 const auto context = QJniObject(QNativeInterface::QAndroidApplication::context());
855 const bool selfUriPermission = context.callMethod<jint>(
"checkCallingOrSelfUriPermission",
856 m_uri.object<QtJniTypes::Uri>(),
857 FLAG_GRANT_WRITE_URI_PERMISSION);
858 if (selfUriPermission != 0)
861 const QString type = mimeType();
865 const int flags = Cursor::queryColumn(m_uri, Document::COLUMN_FLAGS).toInt();
870 const bool isDir = (type == Document::MIME_TYPE_DIR);
873 return dirSupportsCreate || supportsWrite;
878 return deleteDocument(m_uri);
883 return !name().isEmpty();
888 std::vector<DocumentFilePtr> res;
889 const auto childrenUri = buildChildDocumentsUriUsingTree(m_uri, documentId(m_uri));
890 const auto query = Cursor::queryUri(childrenUri, {Document::COLUMN_DOCUMENT_ID});
894 while (query->moveToNext()) {
895 const auto uri = buildDocumentUriUsingTree(m_uri, query->data(0).toString());
896 res.push_back(std::make_shared<MakeableDocumentFile>(uri, shared_from_this()));
905 QJniObject renamedUri = renameDocument(m_uri, newName);
906 if (!renamedUri.isValid())
910 m_displayName = newName;
916 auto destDoc = parseFromAnyUri(newName);
920 DocumentFilePtr destParent = destDoc->parent();
921 const QString oldName = name();
922 const QString destName = QUrl::fromPercentEncoding(QFileInfo(newName).fileName().toUtf8());
925 if (parent() && destParent && parent()->uri().toString() != destParent->uri().toString())
928 QJniObject currentUri = m_uri;
933 currentUri = moveDocument(m_uri, parent()->uri(), destParent->uri());
934 if (!currentUri.isValid())
938 if (oldName != destName) {
939 QJniObject renamedUri = renameDocument(currentUri, destName);
940 if (!renamedUri.isValid())
943 currentUri = renamedUri;
947 m_displayName = destName;
949 m_parent = destParent;
static QJniObject & contentResolverInstance()
constexpr char documentSegment[]
constexpr char contentSchemeFull[]
constexpr char contentScheme[]
constexpr char treeSegment[]
constexpr char childrenSegment[]
QJniObject parseUri(const QString &uri)
std::unique_ptr< QAbstractFileEngine > create(const QString &fileName) const override
If this file handler can handle fileName, this method creates a file engine and returns it wrapped in...
~AndroidContentFileEngineHandler()
bool advance() override
This pure virtual function advances the iterator to the next directory entry; if the operation was su...
~AndroidContentFileEngineIterator()
QString currentFilePath() const override
Returns the path to the current directory entry.
AndroidContentFileEngineIterator(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames)
QString currentFileName() const override
This pure virtual function returns the name of the current directory entry, excluding the path.
QByteArray id() const override
qint64 size() const override
Returns the size of the file.
bool mkdir(const QString &dirName, bool createParentDirectories, std::optional< QFile::Permissions > permissions=std::nullopt) const override
Requests that the directory dirName be created with the specified permissions.
FileFlags fileFlags(FileFlags type=FileInfoAll) const override
This function should return the set of OR'd flags that are true for the file engine's file,...
QDateTime fileTime(QFile::FileTime time) const override
If time is BirthTime, return when the file was born (created).
bool rmdir(const QString &dirName, bool recurseParentDirectories) const override
Requests that the directory dirName is deleted from the file system.
IteratorUniquePtr beginEntryList(const QString &path, QDirListing::IteratorFlags filters, const QStringList &filterNames) override
Returns a QAbstractFileEngine::IteratorUniquePtr, that can be used to iterate over the entries in pat...
bool close() override
Closes the file, returning true if successful; otherwise returns false.
AndroidContentFileEngine(const QString &fileName)
bool rename(const QString &newName) override
Requests that the file be renamed to newName in the file system.
bool remove() override
Requests that the file is deleted from the file system.
QString fileName(FileName file=DefaultName) const override
Return the file engine's current file name in the format specified by file.
Cursor(const QJniObject &object)
QVariant data(int columnIndex) const
static QVariant queryColumn(const QJniObject &uri, const QString &column)
bool isNull(int columnIndex) const
static std::unique_ptr< Cursor > queryUri(const QJniObject &uri, const QStringList &projection={}, const QString &selection={}, const QStringList &selectionArgs={}, const QString &sortOrder={})
const DocumentFilePtr & parent() const
bool rename(const QString &newName)
const QJniObject & uri() const
DocumentFilePtr createFile(const QString &mimeType, const QString &displayName)
DocumentFilePtr createDirectory(const QString &displayName)
std::vector< DocumentFilePtr > listFiles()
QString initialName() const
DocumentFile(const QJniObject &uri, const QString &displayName, const std::shared_ptr< DocumentFile > &parent)
QDateTime lastModified() const
const QLatin1String COLUMN_LAST_MODIFIED("last_modified")
constexpr int FLAG_SUPPORTS_MOVE
const QLatin1String MIME_TYPE_DIR("vnd.android.document/directory")
const QLatin1String COLUMN_MIME_TYPE("mime_type")
const QLatin1String COLUMN_DISPLAY_NAME("_display_name")
const QLatin1String COLUMN_DOCUMENT_ID("document_id")
constexpr int FLAG_VIRTUAL_DOCUMENT
constexpr int FLAG_SUPPORTS_DELETE
const QLatin1String COLUMN_FLAGS("flags")
const QLatin1String COLUMN_SIZE("_size")
constexpr int FLAG_SUPPORTS_RENAME
constexpr int FLAG_DIR_SUPPORTS_CREATE
constexpr int FLAG_SUPPORTS_WRITE
bool isDocumentUri(const QJniObject &uri)
bool isTreeUri(const QJniObject &uri)
bool deleteDocument(const QJniObject &documentUri)
QJniObject renameDocument(const QJniObject &documentUri, const QString &displayName)
QJniObject buildDocumentUriUsingTree(const QJniObject &treeUri, const QString &documentId)
QString treeDocumentId(const QJniObject &uri)
QString documentId(const QJniObject &uri)
QJniObject moveDocument(const QJniObject &sourceDocumentUri, const QJniObject &sourceParentDocumentUri, const QJniObject &targetParentDocumentUri)
QJniObject buildChildDocumentsUriUsingTree(const QJniObject &uri, const QString &parentDocumentId)
QJniObject createDocument(const QJniObject &parentDocumentUri, const QString &mimeType, const QString &displayName)
Combined button and popup list for selecting options.
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
Q_DECLARE_JNI_CLASS(MotionEvent, "android/view/MotionEvent")