Android: fix Android assets handler not listing dirs with only sub dirs

It looks like AAssetDir_getNextFileName is not enough.
Directories that contain only other directories (no files)
were not listed.

On the other hand, AAssetManager_openDir() will always return a
pointer to initialized object (even if the specified directory does not
exists), so we can't just leave only it here.

Using FolderIterator as a last resort. This approach should not be too
time consuming.

As part of this fix, add some unit tests to cover/ensure assets
listing/iterating works as expected.

Fixes: QTBUG-107627
Pick-to: 6.4 6.2 5.15
Change-Id: Id375fe8f99f4ca3f8cad4756f783ffafe5c074df
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Bartlomiej Moskal 2022-11-21 17:28:52 +01:00
parent fbfaf5d38b
commit 4ceee3911a
5 changed files with 35 additions and 1 deletions

View File

@ -108,6 +108,8 @@ public:
FolderIterator(const QString &path)
: m_path(path)
{
// Note that empty dirs in the assets dir before the build are not going to be
// included in the final apk, so no empty folders should expected to be listed.
QJniObject files = QJniObject::callStaticObjectMethod(QtAndroid::applicationClass(),
"listAssetContent",
"(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
@ -350,8 +352,13 @@ public:
} else {
auto *assetDir = AAssetManager_openDir(m_assetManager, m_fileName.toUtf8());
if (assetDir) {
if (AAssetDir_getNextFileName(assetDir))
if (AAssetDir_getNextFileName(assetDir)
|| (!FolderIterator::fromCache(m_fileName, false)->empty())) {
// If AAssetDir_getNextFileName is not valid, it still can be a directory that
// contains only other directories (no files). FolderIterator will not be called
// on the directory containing files so it should not be too time consuming now.
m_assetInfo->type = AssetItem::Type::Folder;
}
AAssetDir_close(assetDir);
}
}

View File

@ -7,9 +7,11 @@
#include <QGuiApplication>
#include <QtCore/qnativeinterface.h>
#include <QtCore/qjniobject.h>
#include <QtCore/qdiriterator.h>
#include <QScreen>
#include <qpa/qplatformscreen.h>
#include <qpa/qplatformnativeinterface.h>
#include <QtCore/qdiriterator.h>
class tst_Android : public QObject
{
@ -17,6 +19,7 @@ Q_OBJECT
private slots:
void assetsRead();
void assetsNotWritable();
void assetsIterating();
void testAndroidSdkVersion();
void testAndroidActivity();
void testRunOnAndroidMainThread();
@ -46,6 +49,27 @@ void tst_Android::assetsNotWritable()
QVERIFY(!file.open(QIODevice::Append));
}
void tst_Android::assetsIterating()
{
QStringList assets = {"assets:/top_level_dir/file_in_top_dir.txt",
"assets:/top_level_dir/sub_dir",
"assets:/top_level_dir/sub_dir/file_in_sub_dir.txt",
"assets:/top_level_dir/sub_dir/sub_dir_2",
"assets:/top_level_dir/sub_dir/sub_dir_2/sub_dir_3",
"assets:/top_level_dir/sub_dir/sub_dir_2/sub_dir_3/file_in_sub_dir_3.txt"};
// Note that we have an "assets:/top_level_dir/sub_dir/empty_sub_dir" in the test's
// assets physical directory, but empty folders are not packaged in the built apk,
// so it's expected to not have such folder be listed in the assets on runtime
QDirIterator it("assets:/top_level_dir", QDirIterator::Subdirectories);
QStringList iteratorAssets;
while (it.hasNext())
iteratorAssets.append(it.next());
QVERIFY(assets == iteratorAssets);
}
void tst_Android::testAndroidSdkVersion()
{
QVERIFY(QNativeInterface::QAndroidApplication::sdkVersion() > 0);