10#include <QCoreApplication>
12#include <QtCore/QJniEnvironment>
13#include <QtCore/QJniObject>
17using namespace Qt::StringLiterals;
24 if (file.startsWith(assetsPrefix))
26 file.replace(
"//"_L1,
"/"_L1);
27 if (file.startsWith(u'/'))
29 if (file.endsWith(u'/'))
36 path = assetsPrefix + u'/' + path;
37 path.replace(
"//"_L1,
"/"_L1);
51 if (name.endsWith(u'/')) {
61using AssetItemList = QList<AssetItem>;
68 QMutexLocker lock(&m_assetsCacheMutex);
69 QSharedPointer<FolderIterator> *folder = m_assetsCache.object(path);
71 folder =
new QSharedPointer<FolderIterator>{
new FolderIterator{path}};
72 if ((*folder)->empty() || !m_assetsCache.insert(path, folder)) {
73 QSharedPointer<FolderIterator> res = *folder;
78 return clone ? QSharedPointer<FolderIterator>{
new FolderIterator{*(*folder)}} : *folder;
83 if (filePath.isEmpty())
85 const QStringList paths = filePath.split(u'/');
88 for (
const auto &path: paths) {
89 auto folder = fromCache(fullPath,
false);
90 auto it = std::lower_bound(folder->begin(), folder->end(), AssetItem{path}, [](
const AssetItem &val,
const AssetItem &assetItem) {
91 return val.name < assetItem.name;
93 if (it == folder->end() || it->name != path)
94 return AssetItem::Type::Invalid;
95 if (!fullPath.isEmpty())
96 fullPath.append(u'/');
114 QJniObject files = QJniObject::callStaticObjectMethod(QtAndroid::applicationClass(),
116 "(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
117 QtAndroid::assets(), QJniObject::fromString(path).object());
118 if (files.isValid()) {
120 jobjectArray jFiles = files.object<jobjectArray>();
121 const jint nFiles = env->GetArrayLength(jFiles);
122 for (
int i = 0; i < nFiles; ++i) {
123 AssetItem item{QJniObject::fromLocalRef(env->GetObjectArrayElement(jFiles, i)).toString()};
124 insert(std::upper_bound(begin(), end(), item, [](
const auto &a,
const auto &b){
125 return a.name < b.name;
129 m_path = assetsPrefix + u'/' + m_path + u'/';
130 m_path.replace(
"//"_L1,
"/"_L1);
135 if (m_index < 0 || m_index >= size())
137 return at(m_index).name;
141 if (m_index < 0 || m_index >= size())
143 return m_path + at(m_index).name;
148 if (!empty() && m_index + 1 < size()) {
158 static QCache<QString, QSharedPointer<FolderIterator>> m_assetsCache;
159 static QMutex m_assetsCacheMutex;
162QCache<QString, QSharedPointer<FolderIterator>>
FolderIterator::m_assetsCache(std::max(50, qEnvironmentVariableIntValue(
"QT_ANDROID_MAX_ASSETS_CACHE_SIZE")));
163Q_CONSTINIT QMutex FolderIterator::m_assetsCacheMutex;
169 const QStringList &nameFilters,
173 m_currentIterator = FolderIterator::fromCache(cleanedAssetPath(path),
true);
183 if (!m_currentIterator)
185 return m_currentIterator->currentFileName();
190 if (!m_currentIterator)
192 return m_currentIterator->currentFilePath();
197 return m_currentIterator ? m_currentIterator->advance() :
false;
201 QSharedPointer<FolderIterator> m_currentIterator;
218 bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions)
override
220 Q_UNUSED(permissions);
222 if (!m_assetInfo || m_assetInfo->type != AssetItem::Type::File || (openMode & QIODevice::WriteOnly))
225 m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
232 AAsset_close(m_assetFile);
242 return m_assetInfo->size;
249 return AAsset_seek(m_assetFile, 0, SEEK_CUR);
256 return pos == AAsset_seek(m_assetFile, pos, SEEK_SET);
263 return AAsset_read(m_assetFile, data, maxlen);
274 FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
277 if (m_assetInfo->type == AssetItem::Type::File)
278 flags = FileType | commonFlags;
279 else if (m_assetInfo->type == AssetItem::Type::Folder)
280 flags = DirectoryType | commonFlags;
292 return prefixedPath(m_fileName);
294 if ((pos = m_fileName.lastIndexOf(u'/')) != -1)
295 return m_fileName.mid(pos + 1);
299 case AbsolutePathName:
300 case CanonicalPathName:
301 if ((pos = m_fileName.lastIndexOf(u'/')) != -1)
302 return prefixedPath(m_fileName.left(pos));
304 return prefixedPath(m_fileName);
312 if (m_fileName == cleanedAssetPath(file))
315 m_fileName = cleanedAssetPath(file);
318 QMutexLocker lock(&m_assetsInfoCacheMutex);
319 QSharedPointer<AssetItem> *assetInfoPtr = m_assetsInfoCache.object(m_fileName);
321 m_assetInfo = *assetInfoPtr;
326 QSharedPointer<AssetItem> *newAssetInfoPtr =
new QSharedPointer<AssetItem>(
new AssetItem);
328 m_assetInfo = *newAssetInfoPtr;
329 m_assetInfo->name = m_fileName;
330 m_assetInfo->type = AssetItem::Type::Invalid;
332 m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
335 m_assetInfo->type = AssetItem::Type::File;
336 m_assetInfo->size = AAsset_getLength(m_assetFile);
338 auto *assetDir = AAssetManager_openDir(m_assetManager, m_fileName.toUtf8());
340 if (AAssetDir_getNextFileName(assetDir)
341 || (!FolderIterator::fromCache(m_fileName,
false)->empty())) {
345 m_assetInfo->type = AssetItem::Type::Folder;
347 AAssetDir_close(assetDir);
351 QMutexLocker lock(&m_assetsInfoCacheMutex);
352 m_assetsInfoCache.insert(m_fileName, newAssetInfoPtr);
356 const QStringList &filterNames)
override
359 if (m_assetInfo && m_assetInfo->type == AssetItem::Type::Folder)
360 return std::make_unique<AndroidAbstractFileEngineIterator>(filters, filterNames, m_fileName);
365 AAsset *m_assetFile =
nullptr;
366 AAssetManager *m_assetManager =
nullptr;
368 QString m_fileName =
"."_L1;
369 QSharedPointer<AssetItem> m_assetInfo;
371 static QCache<QString, QSharedPointer<AssetItem>> m_assetsInfoCache;
372 static QMutex m_assetsInfoCacheMutex;
375QCache<QString, QSharedPointer<AssetItem>>
AndroidAbstractFileEngine::m_assetsInfoCache(std::max(200, qEnvironmentVariableIntValue(
"QT_ANDROID_MAX_FILEINFO_ASSETS_CACHE_SIZE")));
376Q_CONSTINIT QMutex AndroidAbstractFileEngine::m_assetsInfoCacheMutex;
380 m_assetManager = QtAndroid::assetManager();
386 if (fileName.isEmpty())
389 if (!fileName.startsWith(assetsPrefix))
392 QString path = fileName.mid(prefixSize);
393 path.replace(
"//"_L1,
"/"_L1);
394 if (path.startsWith(u'/'))
396 if (path.endsWith(u'/'))
398 return std::make_unique<AndroidAbstractFileEngine>(m_assetManager, path);
bool advance() override
This pure virtual function advances the iterator to the next directory entry; if the operation was su...
AndroidAbstractFileEngineIterator(QDirListing::IteratorFlags filters, const QStringList &nameFilters, const QString &path)
QString currentFileName() const override
This pure virtual function returns the name of the current directory entry, excluding the path.
QString currentFilePath() const override
Returns the path to the current directory entry.
QFileInfo currentFileInfo() const override
The virtual function returns a QFileInfo for the current directory entry.
~AndroidAbstractFileEngine()
void setFileName(const QString &file) override
Sets the file engine's file name to file.
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,...
qint64 size() const override
Returns the size of the file.
qint64 pos() const override
Returns the current file position.
IteratorUniquePtr beginEntryList(const QString &, QDirListing::IteratorFlags filters, const QStringList &filterNames) override
Returns a QAbstractFileEngine::IteratorUniquePtr, that can be used to iterate over the entries in pat...
bool caseSensitive() const override
Should return true if the underlying file system is case-sensitive; otherwise return false.
bool seek(qint64 pos) override
Sets the file position to the given offset.
qint64 read(char *data, qint64 maxlen) override
Reads a number of characters from the file into data.
bool close() override
Closes the file, returning true if successful; otherwise returns false.
bool open(QIODevice::OpenMode openMode, std::optional< QFile::Permissions > permissions) override
Opens the file in the specified mode.
QString fileName(FileName file=DefaultName) const override
Return the file engine's current file name in the format specified by file.
AndroidAbstractFileEngine(AAssetManager *assetManager, const QString &fileName)
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...
static QSharedPointer< FolderIterator > fromCache(const QString &path, bool clone)
QString currentFilePath() const
FolderIterator(const QString &path)
static AssetItem::Type fileType(const QString &filePath)
FolderIterator(const FolderIterator &other)
QString currentFileName() const
static QString prefixedPath(QString path)
static const auto assetsPrefix
static const int prefixSize
static QString cleanedAssetPath(QString file)
AssetItem(const QString &rawName)