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();
267 if (!
fileName.startsWith(
"content"_L1))
293 if (m_index == -1 && m_files.
isEmpty()) {
294 const auto currentPath =
path();
295 if (currentPath.isEmpty())
299 if (iterDoc->isDirectory())
300 for (
const auto &doc : iterDoc->listFiles())
304 return m_index < (m_files.
size() - 1);
309 if (m_index < 0 || m_index > m_files.
size())
311 return m_files.
at(m_index)->name();
316 if (m_index < 0 || m_index > m_files.
size())
318 return m_files.
at(m_index)->uri().toString();
331 if (m_object.isValid())
332 m_object.callMethod<
void>(
"close");
345 int type = m_object.callMethod<jint>(
"getType", columnIndex);
355 columnIndex).toString());
357 auto blob = m_object.callMethod<jbyteArray>(
"getBlob", columnIndex);
359 const auto blobArray = blob.object<jbyteArray>();
360 const int size = env->GetArrayLength(blobArray);
361 const auto byteArray = env->GetByteArrayElements(blobArray,
nullptr);
363 env->ReleaseByteArrayElements(blobArray, byteArray, 0);
378 uri.object<QtJniTypes::UriType>(),
379 projection.isEmpty() ?
380 nullptr : fromStringList(projection).object<QtJniTypes::StringArray>(),
382 selectionArgs.isEmpty() ?
383 nullptr : fromStringList(selectionArgs).object<QtJniTypes::StringArray>(),
384 sortOrder.
isEmpty() ?
nullptr : QJniObject::fromString(sortOrder).object<jstring>());
387 return std::make_unique<Cursor>(
cursor);
396 if (
query->rowCount() != 1 ||
query->columnCount() != 1)
398 query->moveToFirst();
399 return query->data(0);
404 return m_object.callMethod<jboolean>(
"isNull", columnIndex);
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"); }
420 auto array = env->NewObjectArray(
list.
size(), env.findClass(
"java/lang/String"),
nullptr);
422 env->SetObjectArrayElement(
array,
i, QJniObject::fromString(
list[
i]).object());
423 return QJniObject::fromLocalRef(
array);
467 return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
468 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
470 uri.object()).toString();
475 return QJniObject::callStaticMethod<jstring, QtJniTypes::UriType>(
476 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
478 uri.object()).toString();
483 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
484 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
485 "buildChildDocumentsUriUsingTree",
486 uri.object<QtJniTypes::UriType>(),
487 QJniObject::fromString(parentDocumentId).object<jstring>());
493 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
494 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
495 "buildDocumentUriUsingTree",
496 treeUri.object<QtJniTypes::UriType>(),
497 QJniObject::fromString(
documentId).object<jstring>());
502 return QJniObject::callStaticMethod<jboolean>(
503 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
505 QNativeInterface::QAndroidApplication::context(),
506 uri.object<QtJniTypes::UriType>());
511 return QJniObject::callStaticMethod<jboolean>(
512 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
514 uri.object<QtJniTypes::UriType>());
520 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
521 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
524 parentDocumentUri.object<QtJniTypes::UriType>(),
525 QJniObject::fromString(
mimeType).object<jstring>(),
526 QJniObject::fromString(
displayName).object<jstring>());
535 return QJniObject::callStaticMethod<jboolean>(
536 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
539 documentUri.object<QtJniTypes::UriType>());
550 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
551 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
554 sourceDocumentUri.object<QtJniTypes::UriType>(),
555 sourceParentDocumentUri.object<QtJniTypes::UriType>(),
556 targetParentDocumentUri.object<QtJniTypes::UriType>());
565 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
566 QtJniTypes::className<QtJniTypes::DocumentsContract>(),
569 documentUri.object<QtJniTypes::UriType>(),
570 QJniObject::fromString(
displayName).object<jstring>());
600 return QJniObject::callStaticMethod<QtJniTypes::UriType>(
601 QtJniTypes::className<QtJniTypes::Uri>(),
613 const QString documentType =
"/document/"_L1;
614 const QString treeType =
"/tree/"_L1;
616 const int treeIndex =
fileName.indexOf(treeType);
617 const int documentIndex =
fileName.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()));
797 auto lastSeparatorIndex = [](
const QString &
file) {
798 int posDecoded =
file.lastIndexOf(
"/");
800 return posEncoded > posDecoded ? posEncoded : posDecoded;
816 const QString destParent = newName.
left(lastSeparatorIndex(newName));
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.
~AndroidContentFileEngineHandler()
AndroidContentFileEngineHandler()
QString next() override
This pure virtual function advances the iterator to the next directory entry, and returns the file pa...
~AndroidContentFileEngineIterator()
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={})
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\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
qsizetype lastIndexOf(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const noexcept
bool startsWith(const QString &s, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
Returns true if the string starts with s; otherwise returns false.
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.
qsizetype size() const
Returns the number of characters in this string.
QString mid(qsizetype position, qsizetype n=-1) const
Returns a string that contains n characters of this string, starting at the specified position index.
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
bool contains(QChar c, Qt::CaseSensitivity cs=Qt::CaseSensitive) const
QString left(qsizetype n) const
Returns a substring that contains the n leftmost characters of the string.
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
static QByteArray toPercentEncoding(const QString &, const QByteArray &exclude=QByteArray(), const QByteArray &include=QByteArray())
Returns an encoded copy of input.
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 >
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]
IUIAutomationTreeWalker __RPC__deref_out_opt IUIAutomationElement ** parent