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
qandroidassetsfileenginehandler.cpp
Go to the documentation of this file.
1// Copyright (C) 2012 BogDan Vatra <bogdan@kde.org>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "androidjnimain.h"
6
7#include <optional>
8
9#include <QCoreApplication>
10#include <QList>
11#include <QtCore/QJniEnvironment>
12#include <QtCore/QJniObject>
13
15
16using namespace Qt::StringLiterals;
17
18static const auto assetsPrefix = "assets:"_L1;
19const static int prefixSize = 7;
20
22{
23 if (file.startsWith(assetsPrefix))
25 file.replace("//"_L1, "/"_L1);
26 if (file.startsWith(u'/'))
27 file.remove(0, 1);
28 if (file.endsWith(u'/'))
29 file.chop(1);
30 return file;
31}
32
34{
35 path = assetsPrefix + u'/' + path;
36 path.replace("//"_L1, "/"_L1);
37 return path;
38}
39
40struct AssetItem {
41 enum class Type {
42 File,
43 Folder,
45 };
46 AssetItem() = default;
47 AssetItem (const QString &rawName)
48 : name(rawName)
49 {
50 if (name.endsWith(u'/')) {
51 type = Type::Folder;
52 name.chop(1);
53 }
54 }
55 Type type = Type::File;
58};
59
60using AssetItemList = QList<AssetItem>;
61
63{
64public:
65 static QSharedPointer<FolderIterator> fromCache(const QString &path, bool clone)
66 {
67 QMutexLocker lock(&m_assetsCacheMutex);
68 QSharedPointer<FolderIterator> *folder = m_assetsCache.object(path);
69 if (!folder) {
70 folder = new QSharedPointer<FolderIterator>{new FolderIterator{path}};
71 if ((*folder)->empty() || !m_assetsCache.insert(path, folder)) {
72 QSharedPointer<FolderIterator> res = *folder;
73 delete folder;
74 return res;
75 }
76 }
77 return clone ? QSharedPointer<FolderIterator>{new FolderIterator{*(*folder)}} : *folder;
78 }
79
80 static AssetItem::Type fileType(const QString &filePath)
81 {
82 if (filePath.isEmpty())
83 return AssetItem::Type::Folder;
84 const QStringList paths = filePath.split(u'/');
85 QString fullPath;
86 AssetItem::Type res = AssetItem::Type::Invalid;
87 for (const auto &path: paths) {
88 auto folder = fromCache(fullPath, false);
89 auto it = std::lower_bound(folder->begin(), folder->end(), AssetItem{path}, [](const AssetItem &val, const AssetItem &assetItem) {
90 return val.name < assetItem.name;
91 });
92 if (it == folder->end() || it->name != path)
93 return AssetItem::Type::Invalid;
94 if (!fullPath.isEmpty())
95 fullPath.append(u'/');
96 fullPath += path;
97 res = it->type;
98 }
99 return res;
100 }
101
104 , m_index(-1)
105 , m_path(other.m_path)
106 {}
107
109 : m_path(path)
110 {
111 // Note that empty dirs in the assets dir before the build are not going to be
112 // included in the final apk, so no empty folders should expected to be listed.
113 QJniObject files = QJniObject::callStaticObjectMethod(QtAndroid::applicationClass(),
114 "listAssetContent",
115 "(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
116 QtAndroid::assets(), QJniObject::fromString(path).object());
117 if (files.isValid()) {
118 QJniEnvironment env;
119 jobjectArray jFiles = files.object<jobjectArray>();
120 const jint nFiles = env->GetArrayLength(jFiles);
121 for (int i = 0; i < nFiles; ++i) {
122 AssetItem item{QJniObject::fromLocalRef(env->GetObjectArrayElement(jFiles, i)).toString()};
123 insert(std::upper_bound(begin(), end(), item, [](const auto &a, const auto &b){
124 return a.name < b.name;
125 }), item);
126 }
127 }
128 m_path = assetsPrefix + u'/' + m_path + u'/';
129 m_path.replace("//"_L1, "/"_L1);
130 }
131
133 {
134 if (m_index < 0 || m_index >= size())
135 return {};
136 return at(m_index).name;
137 }
139 {
140 if (m_index < 0 || m_index >= size())
141 return {};
142 return m_path + at(m_index).name;
143 }
144
145 bool advance()
146 {
147 if (!empty() && m_index + 1 < size()) {
148 ++m_index;
149 return true;
150 }
151 return false;
152 }
153
154private:
155 int m_index = -1;
156 QString m_path;
157 static QCache<QString, QSharedPointer<FolderIterator>> m_assetsCache;
158 static QMutex m_assetsCacheMutex;
159};
160
161QCache<QString, QSharedPointer<FolderIterator>> FolderIterator::m_assetsCache(std::max(50, qEnvironmentVariableIntValue("QT_ANDROID_MAX_ASSETS_CACHE_SIZE")));
162Q_CONSTINIT QMutex FolderIterator::m_assetsCacheMutex;
163
165{
166public:
174
175 QFileInfo currentFileInfo() const override
176 {
177 return QFileInfo(currentFilePath());
178 }
179
180 QString currentFileName() const override
181 {
182 if (!m_currentIterator)
183 return {};
184 return m_currentIterator->currentFileName();
185 }
186
187 QString currentFilePath() const override
188 {
189 if (!m_currentIterator)
190 return {};
191 return m_currentIterator->currentFilePath();
192 }
193
194 bool advance() override
195 {
196 return m_currentIterator ? m_currentIterator->advance() : false;
197 }
198
199private:
200 QSharedPointer<FolderIterator> m_currentIterator;
201};
202
204{
205public:
206 explicit AndroidAbstractFileEngine(AAssetManager *assetManager, const QString &fileName)
207 : m_assetManager(assetManager)
208 {
210 }
211
213 {
214 close();
215 }
216
217 bool open(QIODevice::OpenMode openMode, std::optional<QFile::Permissions> permissions) override
218 {
219 Q_UNUSED(permissions);
220
221 if (!m_assetInfo || m_assetInfo->type != AssetItem::Type::File || (openMode & QIODevice::WriteOnly))
222 return false;
223 close();
224 m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
225 return m_assetFile;
226 }
227
228 bool close() override
229 {
230 if (m_assetFile) {
231 AAsset_close(m_assetFile);
232 m_assetFile = 0;
233 return true;
234 }
235 return false;
236 }
237
238 qint64 size() const override
239 {
240 if (m_assetInfo)
241 return m_assetInfo->size;
242 return -1;
243 }
244
245 qint64 pos() const override
246 {
247 if (m_assetFile)
248 return AAsset_seek(m_assetFile, 0, SEEK_CUR);
249 return -1;
250 }
251
252 bool seek(qint64 pos) override
253 {
254 if (m_assetFile)
255 return pos == AAsset_seek(m_assetFile, pos, SEEK_SET);
256 return false;
257 }
258
259 qint64 read(char *data, qint64 maxlen) override
260 {
261 if (m_assetFile)
262 return AAsset_read(m_assetFile, data, maxlen);
263 return -1;
264 }
265
266 bool caseSensitive() const override
267 {
268 return true;
269 }
270
271 FileFlags fileFlags(FileFlags type = FileInfoAll) const override
272 {
274 FileFlags flags;
275 if (m_assetInfo) {
276 if (m_assetInfo->type == AssetItem::Type::File)
277 flags = FileType | commonFlags;
278 else if (m_assetInfo->type == AssetItem::Type::Folder)
279 flags = DirectoryType | commonFlags;
280 }
281 return type & flags;
282 }
283
285 {
287 switch (file) {
288 case DefaultName:
289 case AbsoluteName:
290 case CanonicalName:
291 return prefixedPath(m_fileName);
292 case BaseName:
293 if ((pos = m_fileName.lastIndexOf(u'/')) != -1)
294 return m_fileName.mid(pos + 1);
295 else
296 return m_fileName;
297 case PathName:
298 case AbsolutePathName:
300 if ((pos = m_fileName.lastIndexOf(u'/')) != -1)
301 return prefixedPath(m_fileName.left(pos));
302 else
303 return prefixedPath(m_fileName);
304 default:
305 return QString();
306 }
307 }
308
309 void setFileName(const QString &file) override
310 {
311 if (m_fileName == cleanedAssetPath(file))
312 return;
313 close();
314 m_fileName = cleanedAssetPath(file);
315
316 {
317 QMutexLocker lock(&m_assetsInfoCacheMutex);
318 QSharedPointer<AssetItem> *assetInfoPtr = m_assetsInfoCache.object(m_fileName);
319 if (assetInfoPtr) {
320 m_assetInfo = *assetInfoPtr;
321 return;
322 }
323 }
324
325 QSharedPointer<AssetItem> *newAssetInfoPtr = new QSharedPointer<AssetItem>(new AssetItem);
326
327 m_assetInfo = *newAssetInfoPtr;
328 m_assetInfo->name = m_fileName;
329 m_assetInfo->type = AssetItem::Type::Invalid;
330
331 m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
332
333 if (m_assetFile) {
334 m_assetInfo->type = AssetItem::Type::File;
335 m_assetInfo->size = AAsset_getLength(m_assetFile);
336 } else {
337 auto *assetDir = AAssetManager_openDir(m_assetManager, m_fileName.toUtf8());
338 if (assetDir) {
339 if (AAssetDir_getNextFileName(assetDir)
340 || (!FolderIterator::fromCache(m_fileName, false)->empty())) {
341 // If AAssetDir_getNextFileName is not valid, it still can be a directory that
342 // contains only other directories (no files). FolderIterator will not be called
343 // on the directory containing files so it should not be too time consuming now.
344 m_assetInfo->type = AssetItem::Type::Folder;
345 }
346 AAssetDir_close(assetDir);
347 }
348 }
349
350 QMutexLocker lock(&m_assetsInfoCacheMutex);
351 m_assetsInfoCache.insert(m_fileName, newAssetInfoPtr);
352 }
353
355 beginEntryList(const QString &, QDir::Filters filters, const QStringList &filterNames) override
356 {
357 // AndroidAbstractFileEngineIterator use `m_fileName` as the path
358 if (m_assetInfo && m_assetInfo->type == AssetItem::Type::Folder)
359 return std::make_unique<AndroidAbstractFileEngineIterator>(filters, filterNames, m_fileName);
360 return nullptr;
361 }
362
363private:
364 AAsset *m_assetFile = nullptr;
365 AAssetManager *m_assetManager = nullptr;
366 // initialize with a name that can't be used as a file name
367 QString m_fileName = "."_L1;
368 QSharedPointer<AssetItem> m_assetInfo;
369
370 static QCache<QString, QSharedPointer<AssetItem>> m_assetsInfoCache;
371 static QMutex m_assetsInfoCacheMutex;
372};
373
374QCache<QString, QSharedPointer<AssetItem>> AndroidAbstractFileEngine::m_assetsInfoCache(std::max(200, qEnvironmentVariableIntValue("QT_ANDROID_MAX_FILEINFO_ASSETS_CACHE_SIZE")));
375Q_CONSTINIT QMutex AndroidAbstractFileEngine::m_assetsInfoCacheMutex;
376
381
382std::unique_ptr<QAbstractFileEngine>
384{
385 if (fileName.isEmpty())
386 return {};
387
388 if (!fileName.startsWith(assetsPrefix))
389 return {};
390
392 path.replace("//"_L1, "/"_L1);
393 if (path.startsWith(u'/'))
394 path.remove(0, 1);
395 if (path.endsWith(u'/'))
396 path.chop(1);
397 return std::make_unique<AndroidAbstractFileEngine>(m_assetManager, path);
398}
399
bool advance() override
This pure virtual function advances the iterator to the next directory entry; if the operation was su...
AndroidAbstractFileEngineIterator(QDir::Filters 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.
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.
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.
IteratorUniquePtr beginEntryList(const QString &, QDir::Filters filters, const QStringList &filterNames) override
Returns a QAbstractFileEngine::IteratorUniquePtr, that can be used to iterate over the entries in pat...
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)
FolderIterator(const QString &path)
static AssetItem::Type fileType(const QString &filePath)
FolderIterator(const FolderIterator &other)
The QAbstractFileEngineIterator class provides an iterator interface for custom file engines.
QDir::Filters filters() const
Returns the entry filters for this iterator.
QStringList nameFilters() const
Returns the name filters for this iterator.
\inmodule QtCore \reentrant
std::unique_ptr< Iterator > IteratorUniquePtr
FileName
These values are used to request a file name in a particular format.
bool remove()
Removes the file specified by fileName().
Definition qfile.cpp:419
\inmodule QtCore
\inmodule QtCore
Definition qlist.h:75
qsizetype size() const noexcept
Definition qlist.h:397
iterator insert(qsizetype i, parameter_type t)
Definition qlist.h:488
bool empty() const noexcept
Definition qlist.h:685
iterator end()
Definition qlist.h:626
iterator begin()
Definition qlist.h:625
\inmodule QtCore
Definition qmutex.h:313
\inmodule QtCore
Definition qmutex.h:281
iterator end()
Definition qset.h:140
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
QString left(qsizetype n) const &
Definition qstring.h:363
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
void chop(qsizetype n)
Removes n characters from the end of the string.
Definition qstring.cpp:6340
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
QByteArray toUtf8() const &
Definition qstring.h:634
QSet< QString >::iterator it
Combined button and popup list for selecting options.
jobject assets()
jclass applicationClass()
AAssetManager * assetManager()
static QString prefixedPath(QString path)
static const int prefixSize
static const auto assetsPrefix
static QString cleanedAssetPath(QString file)
GLboolean GLboolean GLboolean b
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLenum type
GLsizei const GLuint * paths
GLbitfield flags
GLuint name
GLuint res
GLsizei const GLchar *const * path
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
ptrdiff_t qsizetype
Definition qtypes.h:165
long long qint64
Definition qtypes.h:60
QFile file
[0]
QReadWriteLock lock
[0]
QSharedPointer< T > other(t)
[5]
QStringList files
[8]
const QStringList filters({"Image files (*.png *.xpm *.jpg)", "Text files (*.txt)", "Any files (*)" })
[6]
QGraphicsItem * item
QAction * at
AssetItem()=default
AssetItem(const QString &rawName)
Definition moc.h:23