Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
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_CLASS(ContentResolverType, "android/content/ContentResolver");
20Q_DECLARE_JNI_CLASS(UriType, "android/net/Uri");
21Q_DECLARE_JNI_CLASS(Uri, "android/net/Uri");
22Q_DECLARE_JNI_CLASS(ParcelFileDescriptorType, "android/os/ParcelFileDescriptor");
23Q_DECLARE_JNI_CLASS(CursorType, "android/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
253 const QStringList &filterNames)
254{
255 return std::make_unique<AndroidContentFileEngineIterator>(path, filters, filterNames);
256}
257
260
261std::unique_ptr<QAbstractFileEngine>
263{
264 if (fileName.startsWith("content"_L1))
265 return std::make_unique<AndroidContentFileEngine>(fileName);
266
267 return {};
268
269}
270
276
280
282{
283 if (m_index == -1 && m_files.isEmpty()) {
284 const auto currentPath = path();
285 if (currentPath.isEmpty())
286 return false;
287
288 const auto iterDoc = DocumentFile::parseFromAnyUri(currentPath);
289 if (iterDoc->isDirectory())
290 for (const auto &doc : iterDoc->listFiles())
291 m_files.append(doc);
292 if (m_files.isEmpty())
293 return false;
294 m_index = 0;
295 return true;
296 }
297
298 if (m_index < m_files.size() - 1) {
299 ++m_index;
300 return true;
301 }
302
303 return false;
304}
305
307{
308 if (m_index < 0 || m_index > m_files.size())
309 return QString();
310 return m_files.at(m_index)->name();
311}
312
314{
315 if (m_index < 0 || m_index > m_files.size())
316 return QString();
317 return m_files.at(m_index)->uri().toString();
318}
319
320// Start of Cursor
321
323{
324public:
325 explicit Cursor(const QJniObject &object)
326 : m_object{object} { }
327
329 {
330 if (m_object.isValid())
331 m_object.callMethod<void>("close");
332 }
333
334 enum Type {
335 FIELD_TYPE_NULL = 0x00000000,
336 FIELD_TYPE_INTEGER = 0x00000001,
337 FIELD_TYPE_FLOAT = 0x00000002,
338 FIELD_TYPE_STRING = 0x00000003,
339 FIELD_TYPE_BLOB = 0x00000004
340 };
341
342 QVariant data(int columnIndex) const
343 {
344 int type = m_object.callMethod<jint>("getType", columnIndex);
345 switch (type) {
346 case FIELD_TYPE_NULL:
347 return {};
349 return QVariant::fromValue(m_object.callMethod<jlong>("getLong", columnIndex));
350 case FIELD_TYPE_FLOAT:
351 return QVariant::fromValue(m_object.callMethod<jdouble>("getDouble", columnIndex));
353 return QVariant::fromValue(m_object.callMethod<jstring>("getString",
354 columnIndex).toString());
355 case FIELD_TYPE_BLOB: {
356 auto blob = m_object.callMethod<jbyteArray>("getBlob", columnIndex);
357 QJniEnvironment env;
358 const auto blobArray = blob.object<jbyteArray>();
359 const int size = env->GetArrayLength(blobArray);
360 const auto byteArray = env->GetByteArrayElements(blobArray, nullptr);
361 QByteArray data{reinterpret_cast<const char *>(byteArray), size};
362 env->ReleaseByteArrayElements(blobArray, byteArray, 0);
364 }
365 }
366 return {};
367 }
368
369 static std::unique_ptr<Cursor> queryUri(const QJniObject &uri,
370 const QStringList &projection = {},
371 const QString &selection = {},
372 const QStringList &selectionArgs = {},
373 const QString &sortOrder = {})
374 {
375 auto cursor = contentResolverInstance().callMethod<QtJniTypes::CursorType>(
376 "query",
377 uri.object<QtJniTypes::UriType>(),
378 projection.isEmpty() ?
379 nullptr : fromStringList(projection).object<QtJniTypes::StringArray>(),
380 selection.isEmpty() ? nullptr : QJniObject::fromString(selection).object<jstring>(),
381 selectionArgs.isEmpty() ?
382 nullptr : fromStringList(selectionArgs).object<QtJniTypes::StringArray>(),
383 sortOrder.isEmpty() ? nullptr : QJniObject::fromString(sortOrder).object<jstring>());
384 if (!cursor.isValid())
385 return {};
386 return std::make_unique<Cursor>(cursor);
387 }
388
389 static QVariant queryColumn(const QJniObject &uri, const QString &column)
390 {
391 const auto query = queryUri(uri, {column});
392 if (!query)
393 return {};
394
395 if (query->rowCount() != 1 || query->columnCount() != 1)
396 return {};
397 query->moveToFirst();
398 return query->data(0);
399 }
400
401 bool isNull(int columnIndex) const
402 {
403 return m_object.callMethod<jboolean>("isNull", columnIndex);
404 }
405
406 int columnCount() const { return m_object.callMethod<jint>("getColumnCount"); }
407 int rowCount() const { return m_object.callMethod<jint>("getCount"); }
408 int row() const { return m_object.callMethod<jint>("getPosition"); }
409 bool isFirst() const { return m_object.callMethod<jboolean>("isFirst"); }
410 bool isLast() const { return m_object.callMethod<jboolean>("isLast"); }
411 bool moveToFirst() { return m_object.callMethod<jboolean>("moveToFirst"); }
412 bool moveToLast() { return m_object.callMethod<jboolean>("moveToLast"); }
413 bool moveToNext() { return m_object.callMethod<jboolean>("moveToNext"); }
414
415private:
416 static QJniObject fromStringList(const QStringList &list)
417 {
418 QJniEnvironment env;
419 auto array = env->NewObjectArray(list.size(), env.findClass("java/lang/String"), nullptr);
420 for (int i = 0; i < list.size(); ++i)
421 env->SetObjectArrayElement(array, i, QJniObject::fromString(list[i]).object());
422 return QJniObject::fromLocalRef(array);
423 }
424
425 QJniObject m_object;
426};
427
428// End of Cursor
429
430// Start of DocumentsContract
431
432Q_DECLARE_JNI_CLASS(DocumentsContract, "android/provider/DocumentsContract");
433
444{
445
446namespace Document {
447const QLatin1String COLUMN_DISPLAY_NAME("_display_name");
450const QLatin1String COLUMN_LAST_MODIFIED("last_modified");
453
454constexpr int FLAG_DIR_SUPPORTS_CREATE = 0x00000008;
455constexpr int FLAG_SUPPORTS_DELETE = 0x00000004;
456constexpr int FLAG_SUPPORTS_MOVE = 0x00000100;
457constexpr int FLAG_SUPPORTS_RENAME = 0x00000040;
458constexpr int FLAG_SUPPORTS_WRITE = 0x00000002;
459constexpr int FLAG_VIRTUAL_DOCUMENT = 0x00000200;
460
461const QLatin1String MIME_TYPE_DIR("vnd.android.document/directory");
462} // namespace Document
463
465{
466 return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
467 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
468 "getDocumentId",
469 uri.object()).toString();
470}
471
473{
474 return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
475 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
476 "getTreeDocumentId",
477 uri.object()).toString();
478}
479
481{
482 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
483 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
484 "buildChildDocumentsUriUsingTree",
485 uri.object<QtJniTypes::UriType>(),
486 QJniObject::fromString(parentDocumentId).object<jstring>());
487
488}
489
491{
492 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
493 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
494 "buildDocumentUriUsingTree",
495 treeUri.object<QtJniTypes::UriType>(),
496 QJniObject::fromString(documentId).object<jstring>());
497}
498
499bool isDocumentUri(const QJniObject &uri)
500{
501 return QJniObject::callStaticMethod<jboolean>(
502 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
503 "isDocumentUri",
504 QNativeInterface::QAndroidApplication::context(),
505 uri.object<QtJniTypes::UriType>());
506}
507
508bool isTreeUri(const QJniObject &uri)
509{
510 return QJniObject::callStaticMethod<jboolean>(
511 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
512 "isTreeUri",
513 uri.object<QtJniTypes::UriType>());
514}
515
516QJniObject createDocument(const QJniObject &parentDocumentUri, const QString &mimeType,
517 const QString &displayName)
518{
519 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
520 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
521 "createDocument",
522 contentResolverInstance().object<QtJniTypes::ContentResolverType>(),
523 parentDocumentUri.object<QtJniTypes::UriType>(),
524 QJniObject::fromString(mimeType).object<jstring>(),
525 QJniObject::fromString(displayName).object<jstring>());
526}
527
528bool deleteDocument(const QJniObject &documentUri)
529{
530 const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
532 return {};
533
534 return QJniObject::callStaticMethod<jboolean>(
535 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
536 "deleteDocument",
537 contentResolverInstance().object<QtJniTypes::ContentResolverType>(),
538 documentUri.object<QtJniTypes::UriType>());
539}
540
541QJniObject moveDocument(const QJniObject &sourceDocumentUri,
542 const QJniObject &sourceParentDocumentUri,
543 const QJniObject &targetParentDocumentUri)
544{
545 const int flags = Cursor::queryColumn(sourceDocumentUri, Document::COLUMN_FLAGS).toInt();
547 return {};
548
549 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
550 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
551 "moveDocument",
552 contentResolverInstance().object<QtJniTypes::ContentResolverType>(),
553 sourceDocumentUri.object<QtJniTypes::UriType>(),
554 sourceParentDocumentUri.object<QtJniTypes::UriType>(),
555 targetParentDocumentUri.object<QtJniTypes::UriType>());
556}
557
559{
560 const int flags = Cursor::queryColumn(documentUri, Document::COLUMN_FLAGS).toInt();
562 return {};
563
564 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
565 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
566 "renameDocument",
567 contentResolverInstance().object<QtJniTypes::ContentResolverType>(),
568 documentUri.object<QtJniTypes::UriType>(),
569 QJniObject::fromString(displayName).object<jstring>());
570}
571} // End DocumentsContract namespace
572
573// Start of DocumentFile
574
575using namespace DocumentsContract;
576
577namespace {
578class MakeableDocumentFile : public DocumentFile
579{
580public:
581 MakeableDocumentFile(const QJniObject &uri, const DocumentFilePtr &parent = {})
582 : DocumentFile(uri, parent)
583 {}
584};
585}
586
588 const DocumentFilePtr &parent)
589 : m_uri{uri}
590 , m_parent{parent}
591{}
592
594{
595 QString uriToParse = uri;
596 if (uriToParse.contains(' '))
597 uriToParse.replace(' ', QUrl::toPercentEncoding(" "));
598
599 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
600 QtJniTypes::Traits<QtJniTypes::Uri>::className(),
601 "parse",
602 QJniObject::fromString(uriToParse).object<jstring>());
603}
604
606{
607 const QString encodedUri = QUrl(fileName).toEncoded();
608 const QJniObject uri = parseUri(encodedUri);
609
611 return fromSingleUri(uri);
612
613 const QString documentType = "/document/"_L1;
614 const QString treeType = "/tree/"_L1;
615
616 const int treeIndex = encodedUri.indexOf(treeType);
617 const int documentIndex = encodedUri.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 = encodedUri.left(index);
624 DocumentFilePtr parentDocFile = fromTreeUri(parseUri(parentUrl));
625
626 const QString baseName = encodedUri.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
686
691
696
698{
700}
701
703{
704 const QString type = mimeType();
706}
707
713
715{
717 if (timeVariant.isValid())
718 return QDateTime::fromMSecsSinceEpoch(timeVariant.toLongLong());
719 return {};
720}
721
722int64_t DocumentFile::length() const
723{
724 return Cursor::queryColumn(m_uri, Document::COLUMN_SIZE).toLongLong();
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);
809 else if (displayName.startsWith(QUrl::toPercentEncoding("/")))
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(ContentResolverType, "android/content/ContentResolver")
QJniObject parseUri(const QString &uri)
Q_DECLARE_JNI_TYPE(StringArray, "[Ljava/lang/String;")
std::shared_ptr< class DocumentFile > DocumentFilePtr
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...
bool advance() override
This pure virtual function advances the iterator to the next directory entry; if the operation was su...
QString currentFilePath() const override
Returns the path to the current directory entry.
AndroidContentFileEngineIterator(const QString &path, QDir::Filters 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.
IteratorUniquePtr beginEntryList(const QString &path, QDir::Filters filters, const QStringList &filterNames) override
Returns a QAbstractFileEngine::IteratorUniquePtr, that can be used to iterate over the entries in pat...
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.
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 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.
std::unique_ptr< Iterator > IteratorUniquePtr
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:283
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
@ FileModificationTime
Definition qfiledevice.h:61
\inmodule QtCore
\inmodule QtCore
constexpr bool isEmpty() const noexcept
qsizetype size() const noexcept
Definition qlist.h:397
bool isEmpty() const noexcept
Definition qlist.h:401
const_reference at(qsizetype i) const noexcept
Definition qlist.h:446
void append(parameter_type t)
Definition qlist.h:458
\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:129
QString left(qsizetype n) const &
Definition qstring.h:363
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Definition qstring.cpp:4517
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
Definition qstring.h:296
QString & replace(qsizetype i, qsizetype len, QChar after)
Definition qstring.cpp:3824
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:8218
QString mid(qsizetype position, qsizetype n=-1) const &
Definition qstring.cpp:5300
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
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:3466
\inmodule QtCore
Definition qurl.h:94
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
Definition qurl.cpp:2497
QByteArray toEncoded(FormattingOptions options=FullyEncoded) const
Returns the encoded representation of the URL if it's valid; otherwise an empty QByteArray is returne...
Definition qurl.cpp:2967
static QByteArray toPercentEncoding(const QString &, const QByteArray &exclude=QByteArray(), const QByteArray &include=QByteArray())
Returns an encoded copy of input.
Definition qurl.cpp:3019
\inmodule QtCore
Definition qvariant.h:65
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:536
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:166
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint index
[2]
GLuint object
[3]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLfloat GLfloat f
GLenum type
GLbitfield flags
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:60
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:23