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>
29 if (!contentResolver.isValid()) {
30 contentResolver =
QJniObject(QNativeInterface::QAndroidApplication::context())
31 .callMethod<QtJniTypes::ContentResolverType>(
"getContentResolver");
34 return contentResolver;
38 : m_initialFile(filename),
45 std::optional<QFile::Permissions> permissions)
54 if (!m_documentFile->exists()) {
55 if (
QUrl(m_initialFile).
path().startsWith(
"/tree/"_L1)) {
56 const int lastSeparatorIndex = m_initialFile.
lastIndexOf(
'/');
61 if (!mimeTypes.empty())
64 mimeType =
"application/octet-stream";
66 if (m_documentFile->parent()) {
69 m_documentFile = createdFile;
72 qWarning() <<
"open(): non-existent content URI with a document type provided";
83 QtJniTypes::ParcelFileDescriptorType, QtJniTypes::UriType, jstring>(
85 m_documentFile->uri().object(),
86 QJniObject::fromString(openModeStr).object<jstring>());
91 const auto fd = m_pfd.callMethod<jint>(
"getFd");
94 closeNativeFileDescriptor();
103 closeNativeFileDescriptor();
107void AndroidContentFileEngine::closeNativeFileDescriptor()
109 if (m_pfd.isValid()) {
110 m_pfd.callMethod<
void>(
"close");
117 return m_documentFile->length();
122 return m_documentFile->remove();
127 if (m_documentFile->rename(newName)) {
128 m_initialFile = m_documentFile->uri().toString();
135 std::optional<QFileDevice::Permissions> permissions)
const
140 tmp.
remove(m_initialFile);
143 dirParts.removeAll(
"");
145 if (dirParts.isEmpty())
148 auto createdDir = m_documentFile;
149 bool allDirsCreated =
true;
150 for (
const auto &
dir : dirParts) {
152 bool subDirExists =
false;
154 if (
dir == subDir->name() && subDir->isDirectory()) {
161 createdDir = createdDir->createDirectory(
dir);
163 allDirsCreated =
false;
168 if (!createParentDirectories)
172 return allDirsCreated;
177 if (recurseParentDirectories)
178 qWarning() <<
"rmpath(): Unsupported for Content URIs";
181 bool deleted =
false;
183 if (dirFileName ==
dir->name() &&
dir->isDirectory()) {
194 return m_documentFile->id().toUtf8();
201 return m_documentFile->lastModified();
213 if (!m_documentFile->exists())
217 if (!m_documentFile->canRead())
222 if (m_documentFile->isDirectory()) {
226 if (m_documentFile->canWrite())
241 return m_documentFile->uri().toString();
243 return m_documentFile->name();
255 return std::make_unique<AndroidContentFileEngineIterator>(
path,
filters, filterNames);
261std::unique_ptr<QAbstractFileEngine>
264 if (
fileName.startsWith(
"content"_L1))
265 return std::make_unique<AndroidContentFileEngine>(
fileName);
283 if (m_index == -1 && m_files.
isEmpty()) {
284 const auto currentPath =
path();
285 if (currentPath.isEmpty())
289 if (iterDoc->isDirectory())
290 for (
const auto &doc : iterDoc->listFiles())
298 if (m_index < m_files.
size() - 1) {
308 if (m_index < 0 || m_index > m_files.
size())
310 return m_files.
at(m_index)->name();
315 if (m_index < 0 || m_index > m_files.
size())
317 return m_files.
at(m_index)->uri().toString();
330 if (m_object.isValid())
331 m_object.callMethod<
void>(
"close");
344 int type = m_object.callMethod<jint>(
"getType", columnIndex);
354 columnIndex).toString());
356 auto blob = m_object.callMethod<jbyteArray>(
"getBlob", columnIndex);
358 const auto blobArray = blob.object<jbyteArray>();
359 const int size = env->GetArrayLength(blobArray);
360 const auto byteArray = env->GetByteArrayElements(blobArray,
nullptr);
362 env->ReleaseByteArrayElements(blobArray, byteArray, 0);
377 uri.object<QtJniTypes::UriType>(),
378 projection.isEmpty() ?
379 nullptr : fromStringList(projection).object<QtJniTypes::StringArray>(),
381 selectionArgs.isEmpty() ?
382 nullptr : fromStringList(selectionArgs).object<QtJniTypes::StringArray>(),
383 sortOrder.
isEmpty() ?
nullptr : QJniObject::fromString(sortOrder).object<jstring>());
386 return std::make_unique<Cursor>(
cursor);
395 if (
query->rowCount() != 1 ||
query->columnCount() != 1)
397 query->moveToFirst();
398 return query->data(0);
403 return m_object.callMethod<jboolean>(
"isNull", columnIndex);
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"); }
419 auto array = env->NewObjectArray(
list.
size(), env.findClass(
"java/lang/String"),
nullptr);
421 env->SetObjectArrayElement(
array,
i, QJniObject::fromString(
list[
i]).object());
422 return QJniObject::fromLocalRef(
array);
466 return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
467 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
469 uri.object()).toString();
474 return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
475 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
477 uri.object()).toString();
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>());
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>());
501 return QJniObject::callStaticMethod<jboolean>(
502 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
504 QNativeInterface::QAndroidApplication::context(),
505 uri.object<QtJniTypes::UriType>());
510 return QJniObject::callStaticMethod<jboolean>(
511 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
513 uri.object<QtJniTypes::UriType>());
519 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
520 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
523 parentDocumentUri.object<QtJniTypes::UriType>(),
524 QJniObject::fromString(
mimeType).object<jstring>(),
525 QJniObject::fromString(
displayName).object<jstring>());
534 return QJniObject::callStaticMethod<jboolean>(
535 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
538 documentUri.object<QtJniTypes::UriType>());
549 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
550 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
553 sourceDocumentUri.object<QtJniTypes::UriType>(),
554 sourceParentDocumentUri.object<QtJniTypes::UriType>(),
555 targetParentDocumentUri.object<QtJniTypes::UriType>());
564 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
565 QtJniTypes::Traits<QtJniTypes::DocumentsContract>::className(),
568 documentUri.object<QtJniTypes::UriType>(),
569 QJniObject::fromString(
displayName).object<jstring>());
596 if (uriToParse.contains(
' '))
599 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
600 QtJniTypes::Traits<QtJniTypes::Uri>::className(),
602 QJniObject::fromString(uriToParse).object<jstring>());
613 const QString documentType =
"/document/"_L1;
614 const QString treeType =
"/tree/"_L1;
616 const int treeIndex = encodedUri.
indexOf(treeType);
617 const int documentIndex = encodedUri.indexOf(documentType);
620 if (
index <= treeIndex + treeType.size() ||
index <= documentIndex + documentType.size())
630 if (parentDocFile && parentDocFile->isDirectory())
631 docFile->m_parent = parentDocFile;
638 return std::make_shared<MakeableDocumentFile>(
uri);
655 return std::make_shared<MakeableDocumentFile>(
665 return std::make_shared<MakeableDocumentFile>(
717 if (timeVariant.isValid())
728constexpr int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001;
729constexpr int FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002;
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)
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)
765 return dirSupportsCreate || supportsWrite;
780 std::vector<DocumentFilePtr>
res;
786 while (
query->moveToNext()) {
788 res.push_back(std::make_shared<MakeableDocumentFile>(
uri, shared_from_this()));
796 if (newName.startsWith(
"content://"_L1)) {
797 auto lastSeparatorIndex = [](
const QString &
file) {
798 int posDecoded =
file.lastIndexOf(
"/");
800 return posEncoded > posDecoded ? posEncoded : posDecoded;
805 if (newName.contains(
parent)) {
816 const QString destParent = newName.
left(lastSeparatorIndex(newName));
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...
~AndroidContentFileEngineHandler()
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, 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={})
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\reentrant
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
constexpr bool isEmpty() const noexcept
qsizetype size() const noexcept
bool isEmpty() const noexcept
const_reference at(qsizetype i) const noexcept
void append(parameter_type t)
QList< QMimeType > mimeTypesForFileName(const QString &fileName) const
Returns the MIME types for the file name fileName.
\macro QT_RESTRICTED_CAST_FROM_ASCII
QString left(qsizetype n) const &
qsizetype indexOf(QLatin1StringView s, qsizetype from=0, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
QString & replace(qsizetype i, qsizetype len, QChar after)
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.
QString mid(qsizetype position, qsizetype n=-1) const &
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
QString & remove(qsizetype i, qsizetype len)
Removes n characters from the string, starting at the given position index, and returns a reference t...
QString fileName(ComponentFormattingOptions options=FullyDecoded) const
QByteArray toEncoded(FormattingOptions options=FullyEncoded) const
Returns the encoded representation of the URL if it's valid; otherwise an empty QByteArray is returne...
static QByteArray toPercentEncoding(const QString &, const QByteArray &exclude=QByteArray(), const QByteArray &include=QByteArray())
Returns an encoded copy of input.
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 >
constexpr int FLAG_SUPPORTS_MOVE
const QLatin1String COLUMN_DOCUMENT_ID("document_id")
const QLatin1String COLUMN_LAST_MODIFIED("last_modified")
constexpr int FLAG_VIRTUAL_DOCUMENT
constexpr int FLAG_SUPPORTS_DELETE
const QLatin1String COLUMN_SIZE("_size")
const QLatin1String MIME_TYPE_DIR("vnd.android.document/directory")
const QLatin1String COLUMN_FLAGS("flags")
constexpr int FLAG_SUPPORTS_RENAME
constexpr int FLAG_DIR_SUPPORTS_CREATE
constexpr int FLAG_SUPPORTS_WRITE
const QLatin1String COLUMN_MIME_TYPE("mime_type")
const QLatin1String COLUMN_DISPLAY_NAME("_display_name")
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 QString displayName(CGDirectDisplayID displayID)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum GLenum GLsizei void GLsizei void * column
GLsizei const GLchar *const * path
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
QItemSelection * selection
[0]