Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
androidcontentfileengine.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 Volker Krause <vkrause@kde.org>
2// Copyright (C) 2022 The Qt Company Ltd.
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
6
7#include <QtCore/qcoreapplication.h>
8#include <QtCore/qjnienvironment.h>
9#include <QtCore/qjniobject.h>
10#include <QtCore/qurl.h>
11#include <QtCore/qdatetime.h>
12#include <QtCore/qmimedatabase.h>
13
15
16using namespace QNativeInterface;
17using namespace Qt::StringLiterals;
18
19Q_DECLARE_JNI_TYPE(ContentResolverType, "Landroid/content/ContentResolver;");
20Q_DECLARE_JNI_TYPE(UriType, "Landroid/net/Uri;");
21Q_DECLARE_JNI_CLASS(Uri, "android/net/Uri");
22Q_DECLARE_JNI_TYPE(ParcelFileDescriptorType, "Landroid/os/ParcelFileDescriptor;");
23Q_DECLARE_JNI_TYPE(CursorType, "Landroid/database/Cursor;");
24Q_DECLARE_JNI_TYPE(StringArray, "[Ljava/lang/String;");
25
27{
28 static QJniObject contentResolver;
29 if (!contentResolver.isValid()) {
30 contentResolver = QJniObject(QNativeInterface::QAndroidApplication::context())
31 .callMethod<QtJniTypes::ContentResolverType>("getContentResolver");
32 }
33
34 return contentResolver;
35}
36
38 : m_initialFile(filename),
39 m_documentFile(DocumentFile::parseFromAnyUri(filename))
40{
41 setFileName(filename);
42}
43
44bool AndroidContentFileEngine::open(QIODevice::OpenMode openMode,
45 std::optional<QFile::Permissions> permissions)
46{
47 Q_UNUSED(permissions);
48 QString openModeStr;
49 if (openMode & QFileDevice::ReadOnly) {
50 openModeStr += u'r';
51 }
52 if (openMode & QFileDevice::WriteOnly) {
53 openModeStr += u'w';
54 if (!m_documentFile->exists()) {
55 if (QUrl(m_initialFile).path().startsWith("/tree/"_L1)) {
56 const int lastSeparatorIndex = m_initialFile.lastIndexOf('/');
57 const QString fileName = m_initialFile.mid(lastSeparatorIndex + 1);
58
60 const auto mimeTypes = QMimeDatabase().mimeTypesForFileName(fileName);
61 if (!mimeTypes.empty())
62 mimeType = mimeTypes.first().name();
63 else
64 mimeType = "application/octet-stream";
65
66 if (m_documentFile->parent()) {
67 auto createdFile = m_documentFile->parent()->createFile(mimeType, fileName);
68 if (createdFile)
69 m_documentFile = createdFile;
70 }
71 } else {
72 qWarning() << "open(): non-existent content URI with a document type provided";
73 }
74 }
75 }
76 if (openMode & QFileDevice::Truncate) {
77 openModeStr += u't';
78 } else if (openMode & QFileDevice::Append) {
79 openModeStr += u'a';
80 }
81
82 m_pfd = contentResolverInstance().callMethod<
83 QtJniTypes::ParcelFileDescriptorType, QtJniTypes::UriType, jstring>(
84 "openFileDescriptor",
85 m_documentFile->uri().object(),
86 QJniObject::fromString(openModeStr).object<jstring>());
87
88 if (!m_pfd.isValid())
89 return false;
90
91 const auto fd = m_pfd.callMethod<jint>("getFd");
92
93 if (fd < 0) {
94 closeNativeFileDescriptor();
95 return false;
96 }
97
99}
100
102{
103 closeNativeFileDescriptor();
104 return QFSFileEngine::close();
105}
106
107void AndroidContentFileEngine::closeNativeFileDescriptor()
108{
109 if (m_pfd.isValid()) {
110 m_pfd.callMethod<void>("close");
111 m_pfd = QJniObject();
112 }
113}
114
116{
117 return m_documentFile->length();
118}
119
121{
122 return m_documentFile->remove();
123}
124
126{
127 if (m_documentFile->rename(newName)) {
128 m_initialFile = m_documentFile->uri().toString();
129 return true;
130 }
131 return false;
132}
133
134bool AndroidContentFileEngine::mkdir(const QString &dirName, bool createParentDirectories,
135 std::optional<QFileDevice::Permissions> permissions) const
136{
137 Q_UNUSED(permissions)
138
139 QString tmp = dirName;
140 tmp.remove(m_initialFile);
141
142 QStringList dirParts = tmp.split(u'/');
143 dirParts.removeAll("");
144
145 if (dirParts.isEmpty())
146 return false;
147
148 auto createdDir = m_documentFile;
149 bool allDirsCreated = true;
150 for (const auto &dir : dirParts) {
151 // Find if the sub-dir already exists and then don't re-create it
152 bool subDirExists = false;
153 for (const DocumentFilePtr &subDir : m_documentFile->listFiles()) {
154 if (dir == subDir->name() && subDir->isDirectory()) {
155 createdDir = subDir;
156 subDirExists = true;
157 }
158 }
159
160 if (!subDirExists) {
161 createdDir = createdDir->createDirectory(dir);
162 if (!createdDir) {
163 allDirsCreated = false;
164 break;
165 }
166 }
167
168 if (!createParentDirectories)
169 break;
170 }
171
172 return allDirsCreated;
173}
174
175bool AndroidContentFileEngine::rmdir(const QString &dirName, bool recurseParentDirectories) const
176{
177 if (recurseParentDirectories)
178 qWarning() << "rmpath(): Unsupported for Content URIs";
179
180 const QString dirFileName = QUrl(dirName).fileName();
181 bool deleted = false;
182 for (const DocumentFilePtr &dir : m_documentFile->listFiles()) {
183 if (dirFileName == dir->name() && dir->isDirectory()) {
184 deleted = dir->remove();
185 break;
186 }
187 }
188
189 return deleted;
190}
191
193{
194 return m_documentFile->id().toUtf8();
195}
196
198{
199 switch (time) {
201 return m_documentFile->lastModified();
202 break;
203 default:
204 break;
205 }
206
207 return QDateTime();
208}
209
210AndroidContentFileEngine::FileFlags AndroidContentFileEngine::fileFlags(FileFlags type) const
211{
212 FileFlags flags;
213 if (!m_documentFile->exists())
214 return flags;
215
217 if (!m_documentFile->canRead())
218 return flags;
219
221
222 if (m_documentFile->isDirectory()) {
224 } else {
225 flags |= FileType;
226 if (m_documentFile->canWrite())
228 }
229 return type & flags;
230}
231
233{
234 switch (f) {
235 case PathName:
236 case AbsolutePathName:
238 case DefaultName:
239 case AbsoluteName:
240 case CanonicalName:
241 return m_documentFile->uri().toString();
242 case BaseName:
243 return m_documentFile->name();
244 default:
245 break;
246 }
247
248 return QString();
249}
250
252 const QStringList &filterNames)
253{
254 return new AndroidContentFileEngineIterator(filters, filterNames);
255}
256
258{
259 return nullptr;
260}
261
264
266{
267 if (!fileName.startsWith("content"_L1))
268 return nullptr;
269
271}
272
274 const QStringList &filterNames)
276{
277}
278
280{
281}
282
284{
285 if (!hasNext())
286 return QString();
287 ++m_index;
288 return currentFilePath();
289}
290
292{
293 if (m_index == -1 && m_files.isEmpty()) {
294 const auto currentPath = path();
295 if (currentPath.isEmpty())
296 return false;
297
298 const auto iterDoc = DocumentFile::parseFromAnyUri(currentPath);
299 if (iterDoc->isDirectory())
300 for (const auto &doc : iterDoc->listFiles())
301 m_files.append(doc);
302 }
303
304 return m_index < (m_files.size() - 1);
305}
306
308{
309 if (m_index < 0 || m_index > m_files.size())
310 return QString();
311 return m_files.at(m_index)->name();
312}
313
315{
316 if (m_index < 0 || m_index > m_files.size())
317 return QString();
318 return m_files.at(m_index)->uri().toString();
319}
320
321// Start of Cursor
322
324{
325public:
326 explicit Cursor(const QJniObject &object)
327 : m_object{object} { }
328
330 {
331 if (m_object.isValid())
332 m_object.callMethod<void>("close");
333 }
334
335 enum Type {
336 FIELD_TYPE_NULL = 0x00000000,
337 FIELD_TYPE_INTEGER = 0x00000001,
338 FIELD_TYPE_FLOAT = 0x00000002,
339 FIELD_TYPE_STRING = 0x00000003,
340 FIELD_TYPE_BLOB = 0x00000004
341 };
342
343 QVariant data(int columnIndex) const
344 {
345 int type = m_object.callMethod<jint>("getType", columnIndex);
346 switch (type) {
347 case FIELD_TYPE_NULL:
348 return {};
350 return QVariant::fromValue(m_object.callMethod<jlong>("getLong", columnIndex));
351 case FIELD_TYPE_FLOAT:
352 return QVariant::fromValue(m_object.callMethod<jdouble>("getDouble", columnIndex));
354 return QVariant::fromValue(m_object.callMethod<jstring>("getString",
355 columnIndex).toString());
356 case FIELD_TYPE_BLOB: {
357 auto blob = m_object.callMethod<jbyteArray>("getBlob", columnIndex);
358 QJniEnvironment env;
359 const auto blobArray = blob.object<jbyteArray>();
360 const int size = env->GetArrayLength(blobArray);
361 const auto byteArray = env->GetByteArrayElements(blobArray, nullptr);
362 QByteArray data{reinterpret_cast<const char *>(byteArray), size};
363 env->ReleaseByteArrayElements(blobArray, byteArray, 0);
365 }
366 }
367 return {};
368 }
369
370 static std::unique_ptr<Cursor> queryUri(const QJniObject &uri,
371 const QStringList &projection = {},
372 const QString &selection = {},
373 const QStringList &selectionArgs = {},
374 const QString &sortOrder = {})
375 {
376 auto cursor = contentResolverInstance().callMethod<QtJniTypes::CursorType>(
377 "query",
378 uri.object<QtJniTypes::UriType>(),
379 projection.isEmpty() ?
380 nullptr : fromStringList(projection).object<QtJniTypes::StringArray>(),
381 selection.isEmpty() ? nullptr : QJniObject::fromString(selection).object<jstring>(),
382 selectionArgs.isEmpty() ?
383 nullptr : fromStringList(selectionArgs).object<QtJniTypes::StringArray>(),
384 sortOrder.isEmpty() ? nullptr : QJniObject::fromString(sortOrder).object<jstring>());
385 if (!cursor.isValid())
386 return {};
387 return std::make_unique<Cursor>(cursor);
388 }
389
390 static QVariant queryColumn(const QJniObject &uri, const QString &column)
391 {
392 const auto query = queryUri(uri, {column});
393 if (!query)
394 return {};
395
396 if (query->rowCount() != 1 || query->columnCount() != 1)
397 return {};
398 query->moveToFirst();
399 return query->data(0);
400 }
401
402 bool isNull(int columnIndex) const
403 {
404 return m_object.callMethod<jboolean>("isNull", columnIndex);
405 }
406
407 int columnCount() const { return m_object.callMethod<jint>("getColumnCount"); }
408 int rowCount() const { return m_object.callMethod<jint>("getCount"); }
409 int row() const { return m_object.callMethod<jint>("getPosition"); }
410 bool isFirst() const { return m_object.callMethod<jboolean>("isFirst"); }
411 bool isLast() const { return m_object.callMethod<jboolean>("isLast"); }
412 bool moveToFirst() { return m_object.callMethod<jboolean>("moveToFirst"); }
413 bool moveToLast() { return m_object.callMethod<jboolean>("moveToLast"); }
414 bool moveToNext() { return m_object.callMethod<jboolean>("moveToNext"); }
415
416private:
417 static QJniObject fromStringList(const QStringList &list)
418 {
419 QJniEnvironment env;
420 auto array = env->NewObjectArray(list.size(), env.findClass("java/lang/String"), nullptr);
421 for (int i = 0; i < list.size(); ++i)
422 env->SetObjectArrayElement(array, i, QJniObject::fromString(list[i]).object());
423 return QJniObject::fromLocalRef(array);
424 }
425
426 QJniObject m_object;
427};
428
429// End of Cursor
430
431// Start of DocumentsContract
432
433Q_DECLARE_JNI_CLASS(DocumentsContract, "android/provider/DocumentsContract");
434
445{
446
447namespace Document {
448const QLatin1String COLUMN_DISPLAY_NAME("_display_name");
451const QLatin1String COLUMN_LAST_MODIFIED("last_modified");
454
455constexpr int FLAG_DIR_SUPPORTS_CREATE = 0x00000008;
456constexpr int FLAG_SUPPORTS_DELETE = 0x00000004;
457constexpr int FLAG_SUPPORTS_MOVE = 0x00000100;
458constexpr int FLAG_SUPPORTS_RENAME = 0x00000040;
459constexpr int FLAG_SUPPORTS_WRITE = 0x00000002;
460constexpr int FLAG_VIRTUAL_DOCUMENT = 0x00000200;
461
462const QLatin1String MIME_TYPE_DIR("vnd.android.document/directory");
463} // namespace Document
464
466{
467 return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
468 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
469 "getDocumentId",
470 uri.object()).toString();
471}
472
474{
475 return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
476 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
477 "getTreeDocumentId",
478 uri.object()).toString();
479}
480
482{
483 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
484 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
485 "buildChildDocumentsUriUsingTree",
486 uri.object<QtJniTypes::UriType>(),
487 QJniObject::fromString(parentDocumentId).object<jstring>());
488
489}
490
492{
493 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
494 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
495 "buildDocumentUriUsingTree",
496 treeUri.object<QtJniTypes::UriType>(),
497 QJniObject::fromString(documentId).object<jstring>());
498}
499
500bool isDocumentUri(const QJniObject &uri)
501{
502 return QJniObject::callStaticMethod<jboolean>(
503 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
504 "isDocumentUri",
505 QNativeInterface::QAndroidApplication::context(),
506 uri.object<QtJniTypes::UriType>());
507}
508
509bool isTreeUri(const QJniObject &uri)
510{
511 return QJniObject::callStaticMethod<jboolean>(
512 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
513 "isTreeUri",
514 uri.object<QtJniTypes::UriType>());
515}
516
517QJniObject createDocument(const QJniObject &parentDocumentUri, const QString &mimeType,
518 const QString &displayName)
519{
520 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
521 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
522 "createDocument",
524 parentDocumentUri.object<QtJniTypes::UriType>(),
525 QJniObject::fromString(mimeType).object<jstring>(),
526 QJniObject::fromString(displayName).object<jstring>());
527}
528
529bool deleteDocument(const QJniObject &documentUri)
530{
531 const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
533 return {};
534
535 return QJniObject::callStaticMethod<jboolean>(
536 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
537 "deleteDocument",
539 documentUri.object<QtJniTypes::UriType>());
540}
541
542QJniObject moveDocument(const QJniObject &sourceDocumentUri,
543 const QJniObject &sourceParentDocumentUri,
544 const QJniObject &targetParentDocumentUri)
545{
546 const int flags = Cursor::queryColumn(sourceDocumentUri, Document::COLUMN_FLAGS).toInt();
548 return {};
549
550 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
551 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
552 "moveDocument",
554 sourceDocumentUri.object<QtJniTypes::UriType>(),
555 sourceParentDocumentUri.object<QtJniTypes::UriType>(),
556 targetParentDocumentUri.object<QtJniTypes::UriType>());
557}
558
560{
561 const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
563 return {};
564
565 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
566 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
567 "renameDocument",
569 documentUri.object<QtJniTypes::UriType>(),
570 QJniObject::fromString(displayName).object<jstring>());
571}
572} // End DocumentsContract namespace
573
574// Start of DocumentFile
575
576using namespace DocumentsContract;
577
578namespace {
579class MakeableDocumentFile : public DocumentFile
580{
581public:
582 MakeableDocumentFile(const QJniObject &uri, const DocumentFilePtr &parent = {})
583 : DocumentFile(uri, parent)
584 {}
585};
586}
587
589 const DocumentFilePtr &parent)
590 : m_uri{uri}
591 , m_parent{parent}
592{}
593
595{
596 QString uriToParse = uri;
597 if (uriToParse.contains(' '))
598 uriToParse.replace(' ', QUrl::toPercentEncoding(" "));
599
600 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
601 QtJniTypes::className<QtJniTypes::Uri>(),
602 "parse",
603 QJniObject::fromString(uriToParse).object<jstring>());
604}
605
607{
609
611 return fromSingleUri(uri);
612
613 const QString documentType = "/document/"_L1;
614 const QString treeType = "/tree/"_L1;
615
616 const int treeIndex = fileName.indexOf(treeType);
617 const int documentIndex = fileName.indexOf(documentType);
618 const int index = fileName.lastIndexOf("/");
619
620 if (index <= treeIndex + treeType.size() || index <= documentIndex + documentType.size())
621 return fromTreeUri(uri);
622
623 const QString parentUrl = fileName.left(index);
624 DocumentFilePtr parentDocFile = fromTreeUri(parseUri(parentUrl));
625
626 const QString baseName = fileName.mid(index);
627 const QString fileUrl = parentUrl + QUrl::toPercentEncoding(baseName);
628
629 DocumentFilePtr docFile = std::make_shared<MakeableDocumentFile>(parseUri(fileUrl));
630 if (parentDocFile && parentDocFile->isDirectory())
631 docFile->m_parent = parentDocFile;
632
633 return docFile;
634}
635
637{
638 return std::make_shared<MakeableDocumentFile>(uri);
639}
640
642{
643 QString docId;
644 if (isDocumentUri(treeUri))
645 docId = documentId(treeUri);
646 else
647 docId = treeDocumentId(treeUri);
648
649 return std::make_shared<MakeableDocumentFile>(buildDocumentUriUsingTree(treeUri, docId));
650}
651
653{
654 if (isDirectory()) {
655 return std::make_shared<MakeableDocumentFile>(
657 shared_from_this());
658 }
659 return {};
660}
661
663{
664 if (isDirectory()) {
665 return std::make_shared<MakeableDocumentFile>(
667 shared_from_this());
668 }
669 return {};
670}
671
673{
674 return m_uri;
675}
676
678{
679 return m_parent;
680}
681
683{
685}
686
688{
690}
691
693{
695}
696
698{
700}
701
703{
704 const QString type = mimeType();
706}
707
709{
712}
713
715{
717 if (timeVariant.isValid())
718 return QDateTime::fromMSecsSinceEpoch(timeVariant.toLongLong());
719 return {};
720}
721
722int64_t DocumentFile::length() const
723{
725}
726
727namespace {
728constexpr int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001;
729constexpr int FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002;
730}
731
733{
734 const auto context = QJniObject(QNativeInterface::QAndroidApplication::context());
735 const bool selfUriPermission = context.callMethod<jint>("checkCallingOrSelfUriPermission",
736 m_uri.object<QtJniTypes::UriType>(),
737 FLAG_GRANT_READ_URI_PERMISSION);
738 if (selfUriPermission != 0)
739 return false;
740
741 return !mimeType().isEmpty();
742}
743
745{
746 const auto context = QJniObject(QNativeInterface::QAndroidApplication::context());
747 const bool selfUriPermission = context.callMethod<jint>("checkCallingOrSelfUriPermission",
748 m_uri.object<QtJniTypes::UriType>(),
749 FLAG_GRANT_WRITE_URI_PERMISSION);
750 if (selfUriPermission != 0)
751 return false;
752
753 const QString type = mimeType();
754 if (type.isEmpty())
755 return false;
756
759 return true;
760
761 const bool supportsWrite = (flags & Document::FLAG_SUPPORTS_WRITE);
762 const bool isDir = (type == Document::MIME_TYPE_DIR);
763 const bool dirSupportsCreate = (isDir && (flags & Document::FLAG_DIR_SUPPORTS_CREATE));
764
765 return dirSupportsCreate || supportsWrite;
766}
767
769{
770 return deleteDocument(m_uri);
771}
772
774{
775 return !name().isEmpty();
776}
777
778std::vector<DocumentFilePtr> DocumentFile::listFiles()
779{
780 std::vector<DocumentFilePtr> res;
781 const auto childrenUri = buildChildDocumentsUriUsingTree(m_uri, documentId(m_uri));
782 const auto query = Cursor::queryUri(childrenUri, {Document::COLUMN_DOCUMENT_ID});
783 if (!query)
784 return res;
785
786 while (query->moveToNext()) {
787 const auto uri = buildDocumentUriUsingTree(m_uri, query->data(0).toString());
788 res.push_back(std::make_shared<MakeableDocumentFile>(uri, shared_from_this()));
789 }
790 return res;
791}
792
793bool DocumentFile::rename(const QString &newName)
794{
796 if (newName.startsWith("content://"_L1)) {
797 auto lastSeparatorIndex = [](const QString &file) {
798 int posDecoded = file.lastIndexOf("/");
799 int posEncoded = file.lastIndexOf(QUrl::toPercentEncoding("/"));
800 return posEncoded > posDecoded ? posEncoded : posDecoded;
801 };
802
803 // first try to see if the new file is under the same tree and thus used rename only
804 const QString parent = m_uri.toString().left(lastSeparatorIndex(m_uri.toString()));
805 if (newName.contains(parent)) {
806 QString displayName = newName.mid(lastSeparatorIndex(newName));
807 if (displayName.startsWith('/'))
808 displayName.remove(0, 1);
810 displayName.remove(0, 3);
811
813 } else {
814 // Move
815 QJniObject srcParentUri = fromTreeUri(parseUri(parent))->uri();
816 const QString destParent = newName.left(lastSeparatorIndex(newName));
817 QJniObject targetParentUri = fromTreeUri(parseUri(destParent))->uri();
818 uri = moveDocument(m_uri, srcParentUri, targetParentUri);
819 }
820 } else {
821 uri = renameDocument(m_uri, newName);
822 }
823
824 if (uri.isValid()) {
825 m_uri = uri;
826 return true;
827 }
828
829 return false;
830}
831
833
834// End of DocumentFile
static QJniObject & contentResolverInstance()
Q_DECLARE_JNI_CLASS(Uri, "android/net/Uri")
Q_DECLARE_JNI_TYPE(ContentResolverType, "Landroid/content/ContentResolver;")
QJniObject parseUri(const QString &uri)
std::shared_ptr< class DocumentFile > DocumentFilePtr
QAbstractFileEngine * create(const QString &fileName) const override
Creates a file engine for file fileName.
QString next() override
This pure virtual function advances the iterator to the next directory entry, and returns the file pa...
AndroidContentFileEngineIterator(QDir::Filters filters, const QStringList &filterNames)
QString currentFilePath() const override
Returns the path to the current directory entry.
bool hasNext() const override
This pure virtual function returns true if there is at least one more entry in the current directory ...
QString currentFileName() const override
This pure virtual function returns the name of the current directory entry, excluding the path.
QDateTime fileTime(FileTime time) const override
If time is BirthTime, return when the file was born (created).
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,...
QAbstractFileEngine::Iterator * beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
Returns an instance of a QAbstractFileEngineIterator using filters for entry filtering and filterName...
bool rmdir(const QString &dirName, bool recurseParentDirectories) const override
Requests that the directory dirName is deleted from the file system.
bool close() override
Closes the file, returning true if successful; otherwise returns false.
QAbstractFileEngine::Iterator * endEntryList() override
AndroidContentFileEngine(const QString &fileName)
bool rename(const QString &newName) override
Requests that the file be renamed to newName in the file system.
bool open(QIODevice::OpenMode openMode, std::optional< QFile::Permissions > permissions) override
Opens the file in the specified mode.
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={})
int columnCount() const
DocumentFile Api.
DocumentFilePtr m_parent
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()
static DocumentFilePtr fromTreeUri(const QJniObject &treeUri)
static DocumentFilePtr fromSingleUri(const QJniObject &uri)
static DocumentFilePtr parseFromAnyUri(const QString &filename)
DocumentFile(const QJniObject &uri, const std::shared_ptr< DocumentFile > &parent)
QDateTime lastModified() const
The QAbstractFileEngineIterator class provides an iterator interface for custom file engines.
QString path() const
Returns the path for this iterator.
\inmodule QtCore \reentrant
FileTime
These are used by the fileTime() function.
FileName
These values are used to request a file name in a particular format.
\inmodule QtCore
Definition qbytearray.h:57
\inmodule QtCore\reentrant
Definition qdatetime.h:257
static QDateTime fromMSecsSinceEpoch(qint64 msecs, const QTimeZone &timeZone)
bool close() override
\reimp
bool open(QIODevice::OpenMode openMode, std::optional< QFile::Permissions > permissions) override
\reimp
void setFileName(const QString &file) override
\reimp
\inmodule QtCore
\inmodule QtCore
constexpr bool isEmpty() const noexcept
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
void append(parameter_type t)
Definition qlist.h:441
\inmodule QtCore
QList< QMimeType > mimeTypesForFileName(const QString &fileName) const
Returns the MIME types for the file name fileName.
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:279
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
Definition qstring.cpp:5299
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3794
QStringList split(const QString &sep, Qt::SplitBehavior behavior=Qt::KeepEmptyParts, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Splits the string into substrings wherever sep occurs, and returns the list of those strings.
Definition qstring.cpp:7956
qsizetype size() const
Returns the number of characters in this string.
Definition qstring.h:182
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
Definition qstring.cpp:5204
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.h:1217
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
Definition qstring.cpp:5161
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
Definition qstring.cpp:3435
\inmodule QtCore
Definition qurl.h:94
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2494
static QByteArray toPercentEncoding(const QString &, const QByteArray &exclude=QByteArray(), const QByteArray &include=QByteArray())
Returns an encoded copy of input.
Definition qurl.cpp:3016
\inmodule QtCore
Definition qvariant.h:64
qlonglong toLongLong(bool *ok=nullptr) const
Returns the variant as a long long int if the variant has userType() \l QMetaType::LongLong,...
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
static auto fromValue(T &&value) noexcept(std::is_nothrow_copy_constructible_v< T > &&Private::CanUseInternalSpace< T >) -> std::enable_if_t< std::conjunction_v< std::is_copy_constructible< T >, std::is_destructible< T > >, QVariant >
Definition qvariant.h:531
QCursor cursor
const QLatin1String COLUMN_DOCUMENT_ID("document_id")
const QLatin1String COLUMN_LAST_MODIFIED("last_modified")
const QLatin1String COLUMN_SIZE("_size")
const QLatin1String MIME_TYPE_DIR("vnd.android.document/directory")
const QLatin1String COLUMN_FLAGS("flags")
const QLatin1String COLUMN_MIME_TYPE("mime_type")
const QLatin1String COLUMN_DISPLAY_NAME("_display_name")
DocumentsContract Api.
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.
static void * context
static QString displayName(CGDirectDisplayID displayID)
const char * mimeType
#define qWarning
Definition qlogging.h:162
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint object
[3]
GLfloat GLfloat f
GLenum type
GLbitfield flags
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint64 GLenum GLint fd
GLenum GLenum GLsizei void GLsizei void * column
GLenum query
GLuint res
GLenum array
GLsizei const GLchar *const * path
#define Q_UNUSED(x)
long long qint64
Definition qtypes.h:55
QList< int > list
[14]
QFile file
[0]
QObject::connect nullptr
QString dir
[11]
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
QItemSelection * selection
[0]
Definition moc.h:24
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent