10#include <QCoreApplication>
13#include <QtCore/QJniEnvironment>
14#include <QtCore/QJniObject>
18using namespace Qt::StringLiterals;
25 if (file.startsWith(assetsPrefix))
27 file = QDir::cleanPath(file);
28 if (file.startsWith(u'/'))
37 path = assetsPrefix + u'/' + path;
38 path.replace(
"//"_L1,
"/"_L1);
52 if (name.endsWith(u'/')) {
62using AssetItemList = QList<AssetItem>;
69 QMutexLocker lock(&m_assetsCacheMutex);
70 QSharedPointer<FolderIterator> *folder = m_assetsCache.object(path);
72 folder =
new QSharedPointer<FolderIterator>{
new FolderIterator{path}};
73 if ((*folder)->empty() || !m_assetsCache.insert(path, folder)) {
74 QSharedPointer<FolderIterator> res = *folder;
79 return clone ? QSharedPointer<FolderIterator>{
new FolderIterator{*(*folder)}} : *folder;
84 if (filePath.isEmpty())
86 const QStringList paths = filePath.split(u'/');
89 for (
const auto &path: paths) {
90 auto folder = fromCache(fullPath,
false);
91 auto it = std::lower_bound(folder->begin(), folder->end(), AssetItem{path}, [](
const AssetItem &val,
const AssetItem &assetItem) {
92 return val.name < assetItem.name;
94 if (it == folder->end() || it->name != path)
95 return AssetItem::Type::Invalid;
96 if (!fullPath.isEmpty())
97 fullPath.append(u'/');
115 QJniObject files = QJniObject::callStaticObjectMethod(QtAndroid::applicationClass(),
117 "(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
118 QtAndroid::assets(), QJniObject::fromString(path).object());
119 if (files.isValid()) {
121 jobjectArray jFiles = files.object<jobjectArray>();
122 const jint nFiles = env->GetArrayLength(jFiles);
123 for (
int i = 0; i < nFiles; ++i) {
124 AssetItem item{QJniObject::fromLocalRef(env->GetObjectArrayElement(jFiles, i)).toString()};
125 insert(std::upper_bound(begin(), end(), item, [](
const auto &a,
const auto &b){
126 return a.name < b.name;
130 m_path = assetsPrefix + u'/' + m_path + u'/';
131 m_path.replace(
"//"_L1,
"/"_L1);
136 if (m_index < 0 || m_index >= size())
138 return at(m_index).name;
142 if (m_index < 0 || m_index >= size())
144 return m_path + at(m_index).name;
149 if (!empty() && m_index + 1 < size()) {
159 static QCache<QString, QSharedPointer<FolderIterator>> m_assetsCache;
160 static QMutex m_assetsCacheMutex;
163QCache<QString, QSharedPointer<FolderIterator>>
FolderIterator::m_assetsCache(std::max(50, qEnvironmentVariableIntValue(
"QT_ANDROID_MAX_ASSETS_CACHE_SIZE")));
164Q_CONSTINIT QMutex FolderIterator::m_assetsCacheMutex;
170 const QStringList &nameFilters,
174 m_currentIterator = FolderIterator::fromCache(cleanedAssetPath(path),
true);
184 if (!m_currentIterator)
186 return m_currentIterator->currentFileName();
191 if (!m_currentIterator)
193 return m_currentIterator->currentFilePath();
198 return m_currentIterator ? m_currentIterator->advance() :
false;
202 QSharedPointer<FolderIterator> m_currentIterator;
219 bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions)
override
221 Q_UNUSED(permissions);
223 if (!m_assetInfo || m_assetInfo->type != AssetItem::Type::File || (openMode & QIODevice::WriteOnly))
226 m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
233 AAsset_close(m_assetFile);
243 return m_assetInfo->size;
250 return AAsset_seek(m_assetFile, 0, SEEK_CUR);
257 return pos == AAsset_seek(m_assetFile, pos, SEEK_SET);
264 return AAsset_read(m_assetFile, data, maxlen);
275 FileFlags commonFlags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
278 if (m_assetInfo->type == AssetItem::Type::File)
279 flags = FileType | commonFlags;
280 else if (m_assetInfo->type == AssetItem::Type::Folder)
281 flags = DirectoryType | commonFlags;
293 return prefixedPath(m_fileName);
295 if ((pos = m_fileName.lastIndexOf(u'/')) != -1)
296 return m_fileName.mid(pos + 1);
300 case AbsolutePathName:
301 case CanonicalPathName:
302 if ((pos = m_fileName.lastIndexOf(u'/')) != -1)
303 return prefixedPath(m_fileName.left(pos));
305 return prefixedPath(m_fileName);
313 const QString cleanedFile = cleanedAssetPath(file);
314 if (m_fileName == cleanedFile)
317 m_fileName = cleanedFile;
320 QMutexLocker lock(&m_assetsInfoCacheMutex);
321 QSharedPointer<AssetItem> *assetInfoPtr = m_assetsInfoCache.object(m_fileName);
323 m_assetInfo = *assetInfoPtr;
328 QSharedPointer<AssetItem> *newAssetInfoPtr =
new QSharedPointer<AssetItem>(
new AssetItem);
330 m_assetInfo = *newAssetInfoPtr;
331 m_assetInfo->name = m_fileName;
332 m_assetInfo->type = AssetItem::Type::Invalid;
334 m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
337 m_assetInfo->type = AssetItem::Type::File;
338 m_assetInfo->size = AAsset_getLength(m_assetFile);
340 auto *assetDir = AAssetManager_openDir(m_assetManager, m_fileName.toUtf8());
342 if (AAssetDir_getNextFileName(assetDir)
343 || (!FolderIterator::fromCache(m_fileName,
false)->empty())) {
347 m_assetInfo->type = AssetItem::Type::Folder;
349 AAssetDir_close(assetDir);
353 QMutexLocker lock(&m_assetsInfoCacheMutex);
354 m_assetsInfoCache.insert(m_fileName, newAssetInfoPtr);
358 const QStringList &filterNames)
override
361 if (m_assetInfo && m_assetInfo->type == AssetItem::Type::Folder)
362 return std::make_unique<AndroidAbstractFileEngineIterator>(filters, filterNames, m_fileName);
367 AAsset *m_assetFile =
nullptr;
368 AAssetManager *m_assetManager =
nullptr;
370 QString m_fileName =
"."_L1;
371 QSharedPointer<AssetItem> m_assetInfo;
373 static QCache<QString, QSharedPointer<AssetItem>> m_assetsInfoCache;
374 static QMutex m_assetsInfoCacheMutex;
377QCache<QString, QSharedPointer<AssetItem>>
AndroidAbstractFileEngine::m_assetsInfoCache(std::max(200, qEnvironmentVariableIntValue(
"QT_ANDROID_MAX_FILEINFO_ASSETS_CACHE_SIZE")));
378Q_CONSTINIT QMutex AndroidAbstractFileEngine::m_assetsInfoCacheMutex;
382 m_assetManager = QtAndroid::assetManager();
388 if (fileName.isEmpty())
391 if (!fileName.startsWith(assetsPrefix))
394 return std::make_unique<AndroidAbstractFileEngine>(m_assetManager, fileName);
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)