Android: rework assets support
The new version fix QDirIterators and it lists all the files and dirs. Change-Id: I5a30eedb61ab2397a84365d00f308cda0c194de2 Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@qt.io>
This commit is contained in:
parent
72d62144ab
commit
fb09a8bfcf
@ -1013,6 +1013,25 @@ public class QtNative
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static String[] listAssetContent(android.content.res.AssetManager asset, String path) {
|
||||||
|
String [] list;
|
||||||
|
ArrayList<String> res = new ArrayList<String>();
|
||||||
|
try {
|
||||||
|
list = asset.list(path);
|
||||||
|
if (list.length > 0) {
|
||||||
|
for (String file : list) {
|
||||||
|
try {
|
||||||
|
String[] isDir = asset.list(path.length() > 0 ? path + "/" + file : file);
|
||||||
|
if (isDir != null && isDir.length > 0)
|
||||||
|
file += "/";
|
||||||
|
res.add(file);
|
||||||
|
} catch (Exception e) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {}
|
||||||
|
return res.toArray(new String[res.size()]);
|
||||||
|
}
|
||||||
|
|
||||||
// screen methods
|
// screen methods
|
||||||
public static native void setDisplayMetrics(int screenWidthPixels,
|
public static native void setDisplayMetrics(int screenWidthPixels,
|
||||||
int screenHeightPixels,
|
int screenHeightPixels,
|
||||||
|
@ -75,6 +75,7 @@ static jclass m_applicationClass = nullptr;
|
|||||||
static jobject m_classLoaderObject = nullptr;
|
static jobject m_classLoaderObject = nullptr;
|
||||||
static jmethodID m_loadClassMethodID = nullptr;
|
static jmethodID m_loadClassMethodID = nullptr;
|
||||||
static AAssetManager *m_assetManager = nullptr;
|
static AAssetManager *m_assetManager = nullptr;
|
||||||
|
static jobject m_assets = nullptr;
|
||||||
static jobject m_resourcesObj = nullptr;
|
static jobject m_resourcesObj = nullptr;
|
||||||
static jobject m_activityObject = nullptr;
|
static jobject m_activityObject = nullptr;
|
||||||
static jmethodID m_createSurfaceMethodID = nullptr;
|
static jmethodID m_createSurfaceMethodID = nullptr;
|
||||||
@ -439,6 +440,11 @@ namespace QtAndroid
|
|||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
jobject assets()
|
||||||
|
{
|
||||||
|
return m_assets;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace QtAndroid
|
} // namespace QtAndroid
|
||||||
|
|
||||||
static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
|
static jboolean startQtAndroidPlugin(JNIEnv *env, jobject /*object*/, jstring paramsString, jstring environmentString)
|
||||||
@ -588,6 +594,8 @@ static void terminateQt(JNIEnv *env, jclass /*clazz*/)
|
|||||||
env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
|
env->DeleteGlobalRef(m_RGB_565_BitmapConfigValue);
|
||||||
if (m_bitmapDrawableClass)
|
if (m_bitmapDrawableClass)
|
||||||
env->DeleteGlobalRef(m_bitmapDrawableClass);
|
env->DeleteGlobalRef(m_bitmapDrawableClass);
|
||||||
|
if (m_assets)
|
||||||
|
env->DeleteGlobalRef(m_assets);
|
||||||
m_androidPlatformIntegration = nullptr;
|
m_androidPlatformIntegration = nullptr;
|
||||||
delete m_androidAssetsFileEngineHandler;
|
delete m_androidAssetsFileEngineHandler;
|
||||||
m_androidAssetsFileEngineHandler = nullptr;
|
m_androidAssetsFileEngineHandler = nullptr;
|
||||||
@ -840,7 +848,8 @@ static int registerNatives(JNIEnv *env)
|
|||||||
if (object) {
|
if (object) {
|
||||||
FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
|
FIND_AND_CHECK_CLASS("android/content/ContextWrapper");
|
||||||
GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
|
GET_AND_CHECK_METHOD(methodID, clazz, "getAssets", "()Landroid/content/res/AssetManager;");
|
||||||
m_assetManager = AAssetManager_fromJava(env, env->CallObjectMethod(object, methodID));
|
m_assets = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
|
||||||
|
m_assetManager = AAssetManager_fromJava(env, m_assets);
|
||||||
|
|
||||||
GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
|
GET_AND_CHECK_METHOD(methodID, clazz, "getResources", "()Landroid/content/res/Resources;");
|
||||||
m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
|
m_resourcesObj = env->NewGlobalRef(env->CallObjectMethod(object, methodID));
|
||||||
|
@ -82,6 +82,7 @@ namespace QtAndroid
|
|||||||
double scaledDensity();
|
double scaledDensity();
|
||||||
double pixelDensity();
|
double pixelDensity();
|
||||||
JavaVM *javaVM();
|
JavaVM *javaVM();
|
||||||
|
jobject assets();
|
||||||
AAssetManager *assetManager();
|
AAssetManager *assetManager();
|
||||||
jclass applicationClass();
|
jclass applicationClass();
|
||||||
jobject activity();
|
jobject activity();
|
||||||
|
@ -39,40 +39,139 @@
|
|||||||
|
|
||||||
#include "qandroidassetsfileenginehandler.h"
|
#include "qandroidassetsfileenginehandler.h"
|
||||||
#include "androidjnimain.h"
|
#include "androidjnimain.h"
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
|
#include <QtCore/private/qjni_p.h>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
typedef QVector<QString> FilesList;
|
static const QLatin1String assetsPrefix("assets:");
|
||||||
|
const static int prefixSize = 7;
|
||||||
|
|
||||||
struct AndroidAssetDir
|
static inline QString cleanedAssetPath(QString file)
|
||||||
{
|
{
|
||||||
AndroidAssetDir(AAssetDir* ad)
|
if (file.startsWith(assetsPrefix))
|
||||||
|
file.remove(0, prefixSize);
|
||||||
|
file.replace(QLatin1String("//"), QLatin1String("/"));
|
||||||
|
if (file.startsWith(QLatin1Char('/')))
|
||||||
|
file.remove(0, 1);
|
||||||
|
if (file.endsWith(QLatin1Char('/')))
|
||||||
|
file.chop(1);
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString prefixedPath(QString path)
|
||||||
{
|
{
|
||||||
if (ad) {
|
path = assetsPrefix + QLatin1Char('/') + path;
|
||||||
const char *fileName;
|
path.replace(QLatin1String("//"), QLatin1String("/"));
|
||||||
while ((fileName = AAssetDir_getNextFileName(ad)))
|
return path;
|
||||||
m_items.push_back(QString::fromUtf8(fileName));
|
|
||||||
AAssetDir_close(ad);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
FilesList m_items;
|
struct AssetItem {
|
||||||
|
enum class Type {
|
||||||
|
File,
|
||||||
|
Folder
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AssetItem (const QString &rawName)
|
||||||
|
: name(rawName)
|
||||||
|
{
|
||||||
|
if (name.endsWith(QLatin1Char('/'))) {
|
||||||
|
type = Type::Folder;
|
||||||
|
name.chop(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Type type = Type::File;
|
||||||
|
QString name;
|
||||||
|
};
|
||||||
|
|
||||||
|
using AssetItemList = QVector<AssetItem>;
|
||||||
|
|
||||||
|
class FolderIterator : public AssetItemList
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static QSharedPointer<FolderIterator> fromCache(const QString &path)
|
||||||
|
{
|
||||||
|
QMutexLocker lock(&m_assetsCacheMutex);
|
||||||
|
QSharedPointer<FolderIterator> *folder = m_assetsCache.object(path);
|
||||||
|
if (!folder) {
|
||||||
|
folder = new QSharedPointer<FolderIterator>{new FolderIterator{path}};
|
||||||
|
if (!m_assetsCache.insert(path, folder)) {
|
||||||
|
QSharedPointer<FolderIterator> res = *folder;
|
||||||
|
delete folder;
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return *folder;
|
||||||
|
}
|
||||||
|
|
||||||
|
FolderIterator(const QString &path)
|
||||||
|
: m_path(path)
|
||||||
|
{
|
||||||
|
QJNIObjectPrivate files = QJNIObjectPrivate::callStaticObjectMethod(QtAndroid::applicationClass(),
|
||||||
|
"listAssetContent",
|
||||||
|
"(Landroid/content/res/AssetManager;Ljava/lang/String;)[Ljava/lang/String;",
|
||||||
|
QtAndroid::assets(), QJNIObjectPrivate::fromString(path).object());
|
||||||
|
if (files.isValid()) {
|
||||||
|
QJNIEnvironmentPrivate env;
|
||||||
|
jobjectArray jFiles = static_cast<jobjectArray>(files.object());
|
||||||
|
const jint nFiles = env->GetArrayLength(jFiles);
|
||||||
|
for (int i = 0; i < nFiles; ++i)
|
||||||
|
push_back({QJNIObjectPrivate(env->GetObjectArrayElement(jFiles, i)).toString()});
|
||||||
|
}
|
||||||
|
m_path = assetsPrefix + QLatin1Char('/') + m_path + QLatin1Char('/');
|
||||||
|
m_path.replace(QLatin1String("//"), QLatin1String("/"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QString currentFileName() const
|
||||||
|
{
|
||||||
|
if (m_index < 0 || m_index >= size())
|
||||||
|
return {};
|
||||||
|
return at(m_index).name;
|
||||||
|
}
|
||||||
|
QString currentFilePath() const
|
||||||
|
{
|
||||||
|
if (m_index < 0 || m_index >= size())
|
||||||
|
return {};
|
||||||
|
return m_path + at(m_index).name;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasNext() const
|
||||||
|
{
|
||||||
|
return !empty() && m_index + 1 < size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<std::pair<QString, AssetItem>> next()
|
||||||
|
{
|
||||||
|
if (!hasNext())
|
||||||
|
return {};
|
||||||
|
++m_index;
|
||||||
|
return std::pair<QString, AssetItem>(currentFileName(), at(m_index));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
int m_index = -1;
|
||||||
|
QString m_path;
|
||||||
|
static QCache<QString, QSharedPointer<FolderIterator>> m_assetsCache;
|
||||||
|
static QMutex m_assetsCacheMutex;
|
||||||
|
};
|
||||||
|
|
||||||
|
QCache<QString, QSharedPointer<FolderIterator>> FolderIterator::m_assetsCache(std::max(50, qEnvironmentVariableIntValue("QT_ANDROID_MAX_ASSETS_CACHE_SIZE")));
|
||||||
|
QMutex FolderIterator::m_assetsCacheMutex;
|
||||||
|
|
||||||
class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator
|
class AndroidAbstractFileEngineIterator: public QAbstractFileEngineIterator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AndroidAbstractFileEngineIterator(QDir::Filters filters,
|
AndroidAbstractFileEngineIterator(QDir::Filters filters,
|
||||||
const QStringList &nameFilters,
|
const QStringList &nameFilters,
|
||||||
QSharedPointer<AndroidAssetDir> asset,
|
|
||||||
const QString &path)
|
const QString &path)
|
||||||
: QAbstractFileEngineIterator(filters, nameFilters)
|
: QAbstractFileEngineIterator(filters, nameFilters)
|
||||||
{
|
{
|
||||||
m_items = asset->m_items;
|
m_stack.push_back(FolderIterator::fromCache(cleanedAssetPath(path)));
|
||||||
m_index = -1;
|
if (m_stack.last()->empty())
|
||||||
m_path = path;
|
m_stack.pop_back();
|
||||||
}
|
}
|
||||||
|
|
||||||
QFileInfo currentFileInfo() const override
|
QFileInfo currentFileInfo() const override
|
||||||
@ -82,54 +181,59 @@ public:
|
|||||||
|
|
||||||
QString currentFileName() const override
|
QString currentFileName() const override
|
||||||
{
|
{
|
||||||
if (m_index < 0 || m_index >= m_items.size())
|
if (!m_currentIterator)
|
||||||
return QString();
|
return {};
|
||||||
QString fileName = m_items[m_index];
|
return m_currentIterator->currentFileName();
|
||||||
if (fileName.endsWith(QLatin1Char('/')))
|
|
||||||
fileName.chop(1);
|
|
||||||
return fileName;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual QString currentFilePath() const
|
virtual QString currentFilePath() const
|
||||||
{
|
{
|
||||||
return m_path + currentFileName();
|
if (!m_currentIterator)
|
||||||
|
return {};
|
||||||
|
return m_currentIterator->currentFilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasNext() const override
|
bool hasNext() const override
|
||||||
{
|
{
|
||||||
return m_items.size() && (m_index < m_items.size() - 1);
|
if (m_stack.empty())
|
||||||
|
return false;
|
||||||
|
if (!m_stack.last()->hasNext()) {
|
||||||
|
m_stack.pop_back();
|
||||||
|
return hasNext();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
QString next() override
|
QString next() override
|
||||||
{
|
{
|
||||||
if (!hasNext())
|
if (m_stack.empty()) {
|
||||||
return QString();
|
m_currentIterator.reset();
|
||||||
m_index++;
|
return {};
|
||||||
return currentFileName();
|
}
|
||||||
|
m_currentIterator = m_stack.last();
|
||||||
|
auto res = m_currentIterator->next();
|
||||||
|
if (!res)
|
||||||
|
return {};
|
||||||
|
if (res->second.type == AssetItem::Type::Folder) {
|
||||||
|
m_stack.push_back(FolderIterator::fromCache(cleanedAssetPath(currentFilePath())));
|
||||||
|
if (m_stack.last()->empty())
|
||||||
|
m_stack.pop_back();
|
||||||
|
}
|
||||||
|
return res->first;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
QString m_path;
|
mutable QSharedPointer<FolderIterator> m_currentIterator;
|
||||||
FilesList m_items;
|
mutable QVector<QSharedPointer<FolderIterator>> m_stack;
|
||||||
int m_index;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class AndroidAbstractFileEngine: public QAbstractFileEngine
|
class AndroidAbstractFileEngine: public QAbstractFileEngine
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AndroidAbstractFileEngine(AAsset *asset, const QString &fileName)
|
explicit AndroidAbstractFileEngine(AAssetManager *assetManager, const QString &fileName)
|
||||||
|
: m_assetManager(assetManager)
|
||||||
{
|
{
|
||||||
m_assetFile = asset;
|
setFileName(fileName);
|
||||||
m_fileName = fileName;
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit AndroidAbstractFileEngine(QSharedPointer<AndroidAssetDir> asset, const QString &fileName)
|
|
||||||
{
|
|
||||||
m_assetFile = 0;
|
|
||||||
m_assetDir = asset;
|
|
||||||
m_fileName = fileName;
|
|
||||||
if (!m_fileName.endsWith(QLatin1Char('/')))
|
|
||||||
m_fileName += QLatin1Char('/');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
~AndroidAbstractFileEngine()
|
~AndroidAbstractFileEngine()
|
||||||
@ -139,7 +243,11 @@ public:
|
|||||||
|
|
||||||
bool open(QIODevice::OpenMode openMode) override
|
bool open(QIODevice::OpenMode openMode) override
|
||||||
{
|
{
|
||||||
return m_assetFile != 0 && (openMode & QIODevice::WriteOnly) == 0;
|
if (m_isFolder || (openMode & QIODevice::WriteOnly))
|
||||||
|
return false;
|
||||||
|
close();
|
||||||
|
m_assetFile = AAssetManager_open(m_assetManager, m_fileName.toUtf8(), AASSET_MODE_BUFFER);
|
||||||
|
return m_assetFile;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool close() override
|
bool close() override
|
||||||
@ -200,7 +308,7 @@ public:
|
|||||||
FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
|
FileFlags flags(ReadOwnerPerm|ReadUserPerm|ReadGroupPerm|ReadOtherPerm|ExistsFlag);
|
||||||
if (m_assetFile)
|
if (m_assetFile)
|
||||||
flags |= FileType;
|
flags |= FileType;
|
||||||
if (!m_assetDir.isNull())
|
else if (m_isFolder)
|
||||||
flags |= DirectoryType;
|
flags |= DirectoryType;
|
||||||
|
|
||||||
return type & flags;
|
return type & flags;
|
||||||
@ -213,19 +321,19 @@ public:
|
|||||||
case DefaultName:
|
case DefaultName:
|
||||||
case AbsoluteName:
|
case AbsoluteName:
|
||||||
case CanonicalName:
|
case CanonicalName:
|
||||||
return m_fileName;
|
return prefixedPath(m_fileName);
|
||||||
case BaseName:
|
case BaseName:
|
||||||
if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
|
if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
|
||||||
return m_fileName.mid(pos);
|
return prefixedPath(m_fileName.mid(pos));
|
||||||
else
|
else
|
||||||
return m_fileName;
|
return prefixedPath(m_fileName);
|
||||||
case PathName:
|
case PathName:
|
||||||
case AbsolutePathName:
|
case AbsolutePathName:
|
||||||
case CanonicalPathName:
|
case CanonicalPathName:
|
||||||
if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
|
if ((pos = m_fileName.lastIndexOf(QChar(QLatin1Char('/')))) != -1)
|
||||||
return m_fileName.left(pos);
|
return prefixedPath(m_fileName.left(pos));
|
||||||
else
|
else
|
||||||
return m_fileName;
|
return prefixedPath(m_fileName);
|
||||||
default:
|
default:
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
@ -233,164 +341,46 @@ public:
|
|||||||
|
|
||||||
void setFileName(const QString &file) override
|
void setFileName(const QString &file) override
|
||||||
{
|
{
|
||||||
if (file == m_fileName)
|
|
||||||
return;
|
|
||||||
|
|
||||||
m_fileName = file;
|
|
||||||
if (!m_fileName.endsWith(QLatin1Char('/')))
|
|
||||||
m_fileName += QLatin1Char('/');
|
|
||||||
|
|
||||||
close();
|
close();
|
||||||
|
m_fileName = cleanedAssetPath(file);
|
||||||
|
m_isFolder = !open(QIODevice::ReadOnly) && !FolderIterator::fromCache(m_fileName)->empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
|
Iterator *beginEntryList(QDir::Filters filters, const QStringList &filterNames) override
|
||||||
{
|
{
|
||||||
if (!m_assetDir.isNull())
|
if (m_isFolder)
|
||||||
return new AndroidAbstractFileEngineIterator(filters, filterNames, m_assetDir, m_fileName);
|
return new AndroidAbstractFileEngineIterator(filters, filterNames, m_fileName);
|
||||||
return 0;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AAsset *m_assetFile;
|
AAsset *m_assetFile = nullptr;
|
||||||
QSharedPointer<AndroidAssetDir> m_assetDir;
|
AAssetManager *m_assetManager;
|
||||||
QString m_fileName;
|
QString m_fileName;
|
||||||
|
bool m_isFolder;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler()
|
AndroidAssetsFileEngineHandler::AndroidAssetsFileEngineHandler()
|
||||||
: m_assetsCache(std::max(5, qEnvironmentVariableIntValue("QT_ANDROID_MAX_ASSETS_CACHE_SIZE")))
|
|
||||||
, m_hasPrepopulatedCache(false)
|
|
||||||
, m_hasTriedPrepopulatingCache(false)
|
|
||||||
{
|
{
|
||||||
m_assetManager = QtAndroid::assetManager();
|
m_assetManager = QtAndroid::assetManager();
|
||||||
}
|
}
|
||||||
|
|
||||||
AndroidAssetsFileEngineHandler::~AndroidAssetsFileEngineHandler()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void AndroidAssetsFileEngineHandler::prepopulateCache() const
|
|
||||||
{
|
|
||||||
Q_ASSERT(!m_hasTriedPrepopulatingCache);
|
|
||||||
m_hasTriedPrepopulatingCache = true;
|
|
||||||
|
|
||||||
Q_ASSERT(m_assetsCache.isEmpty());
|
|
||||||
|
|
||||||
// Failsafe: Don't read cache files that are larger than 1MB
|
|
||||||
static qint64 maxPrepopulatedCacheSize = qMax(1024LL * 1024LL,
|
|
||||||
qgetenv("QT_ANDROID_MAX_PREPOPULATED_ASSETS_CACHE_SIZE").toLongLong());
|
|
||||||
|
|
||||||
const char *fileName = "--Added-by-androiddeployqt--/qt_cache_pregenerated_file_list";
|
|
||||||
AAsset *asset = AAssetManager_open(m_assetManager, fileName, AASSET_MODE_BUFFER);
|
|
||||||
if (asset) {
|
|
||||||
m_hasPrepopulatedCache = true;
|
|
||||||
AndroidAbstractFileEngine fileEngine(asset, QString::fromLatin1(fileName));
|
|
||||||
if (fileEngine.open(QIODevice::ReadOnly)) {
|
|
||||||
qint64 size = fileEngine.size();
|
|
||||||
|
|
||||||
if (size <= maxPrepopulatedCacheSize) {
|
|
||||||
QByteArray bytes(size, Qt::Uninitialized);
|
|
||||||
qint64 read = fileEngine.read(bytes.data(), size);
|
|
||||||
if (read != size) {
|
|
||||||
qWarning("Failed to read prepopulated cache");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDataStream stream(&bytes, QIODevice::ReadOnly);
|
|
||||||
stream.setVersion(QDataStream::Qt_5_3);
|
|
||||||
if (stream.status() != QDataStream::Ok) {
|
|
||||||
qWarning("Failed to read prepopulated cache");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (!stream.atEnd()) {
|
|
||||||
QString directoryName;
|
|
||||||
stream >> directoryName;
|
|
||||||
|
|
||||||
int fileCount;
|
|
||||||
stream >> fileCount;
|
|
||||||
|
|
||||||
QVector<QString> fileList;
|
|
||||||
fileList.reserve(fileCount);
|
|
||||||
while (fileCount--) {
|
|
||||||
QString fileName;
|
|
||||||
stream >> fileName;
|
|
||||||
fileList.append(fileName);
|
|
||||||
}
|
|
||||||
|
|
||||||
QSharedPointer<AndroidAssetDir> *aad = new QSharedPointer<AndroidAssetDir>(new AndroidAssetDir(0));
|
|
||||||
(*aad)->m_items = fileList;
|
|
||||||
|
|
||||||
// Cost = 0, because we should always cache everything if there's a prepopulated cache
|
|
||||||
QByteArray key = directoryName != QLatin1String("/")
|
|
||||||
? QByteArray("assets:/") + directoryName.toUtf8()
|
|
||||||
: QByteArray("assets:");
|
|
||||||
|
|
||||||
bool ok = m_assetsCache.insert(key, aad, 0);
|
|
||||||
if (!ok)
|
|
||||||
qWarning("Failed to insert in cache: %s", qPrintable(directoryName));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
qWarning("Prepopulated cache is too large to read.\n"
|
|
||||||
"Use environment variable QT_ANDROID_MAX_PREPOPULATED_ASSETS_CACHE_SIZE to adjust size.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QAbstractFileEngine * AndroidAssetsFileEngineHandler::create(const QString &fileName) const
|
QAbstractFileEngine * AndroidAssetsFileEngineHandler::create(const QString &fileName) const
|
||||||
{
|
{
|
||||||
if (fileName.isEmpty())
|
if (fileName.isEmpty())
|
||||||
return 0;
|
return nullptr;
|
||||||
|
|
||||||
static QLatin1String assetsPrefix("assets:");
|
|
||||||
if (!fileName.startsWith(assetsPrefix))
|
if (!fileName.startsWith(assetsPrefix))
|
||||||
return 0;
|
return nullptr;
|
||||||
|
|
||||||
static int prefixSize = assetsPrefix.size() + 1;
|
QString path = fileName.mid(prefixSize);
|
||||||
|
path.replace(QLatin1String("//"), QLatin1String("/"));
|
||||||
QByteArray path;
|
if (path.startsWith(QLatin1Char('/')))
|
||||||
if (!fileName.endsWith(QLatin1Char('/'))) {
|
path.remove(0, 1);
|
||||||
path = fileName.toUtf8();
|
if (path.endsWith(QLatin1Char('/')))
|
||||||
if (path.size() > prefixSize) {
|
path.chop(1);
|
||||||
AAsset *asset = AAssetManager_open(m_assetManager,
|
return new AndroidAbstractFileEngine(m_assetManager, path);
|
||||||
path.constData() + prefixSize,
|
|
||||||
AASSET_MODE_BUFFER);
|
|
||||||
if (asset)
|
|
||||||
return new AndroidAbstractFileEngine(asset, fileName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!path.size())
|
|
||||||
path = fileName.left(fileName.length() - 1).toUtf8();
|
|
||||||
|
|
||||||
|
|
||||||
m_assetsCacheMutext.lock();
|
|
||||||
if (!m_hasTriedPrepopulatingCache)
|
|
||||||
prepopulateCache();
|
|
||||||
|
|
||||||
QSharedPointer<AndroidAssetDir> *aad = m_assetsCache.object(path);
|
|
||||||
m_assetsCacheMutext.unlock();
|
|
||||||
if (!aad) {
|
|
||||||
if (!m_hasPrepopulatedCache && path.size() > prefixSize) {
|
|
||||||
AAssetDir *assetDir = AAssetManager_openDir(m_assetManager, path.constData() + prefixSize);
|
|
||||||
if (assetDir) {
|
|
||||||
if (AAssetDir_getNextFileName(assetDir)) {
|
|
||||||
AAssetDir_rewind(assetDir);
|
|
||||||
aad = new QSharedPointer<AndroidAssetDir>(new AndroidAssetDir(assetDir));
|
|
||||||
m_assetsCacheMutext.lock();
|
|
||||||
m_assetsCache.insert(path, aad);
|
|
||||||
m_assetsCacheMutext.unlock();
|
|
||||||
return new AndroidAbstractFileEngine(*aad, fileName);
|
|
||||||
} else {
|
|
||||||
AAssetDir_close(assetDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return new AndroidAbstractFileEngine(*aad, fileName);
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
@ -49,22 +49,14 @@
|
|||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
|
|
||||||
struct AndroidAssetDir;
|
|
||||||
class AndroidAssetsFileEngineHandler: public QAbstractFileEngineHandler
|
class AndroidAssetsFileEngineHandler: public QAbstractFileEngineHandler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
AndroidAssetsFileEngineHandler();
|
AndroidAssetsFileEngineHandler();
|
||||||
virtual ~AndroidAssetsFileEngineHandler();
|
|
||||||
QAbstractFileEngine *create(const QString &fileName) const override;
|
QAbstractFileEngine *create(const QString &fileName) const override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void prepopulateCache() const;
|
|
||||||
|
|
||||||
AAssetManager *m_assetManager;
|
AAssetManager *m_assetManager;
|
||||||
mutable QCache<QByteArray, QSharedPointer<AndroidAssetDir>> m_assetsCache;
|
|
||||||
mutable QMutex m_assetsCacheMutext;
|
|
||||||
mutable bool m_hasPrepopulatedCache;
|
|
||||||
mutable bool m_hasTriedPrepopulatingCache;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
Loading…
Reference in New Issue
Block a user