Commit 91d48fe7 authored by Eskil Abrahamsen Blomfeldt's avatar Eskil Abrahamsen Blomfeldt Committed by Eike Ziller

Enable mechanism to bundle Qt in APK

If the Qt version built against is Qt 5, and the deployment
method is "Use libs on device" + "Use local Qt libs", Creator
will copy the required files into the app bundle and set
the necessary meta-data to make the Java code in the app
actually load them from the app bundle.

We also make this deployment method the default on Qt 5.

Change-Id: Ib7a33e7d1fbd22f76c85c31e1dbc68912a38eda8
Reviewed-by: default avatarDaniel Teske <daniel.teske@digia.com>
Reviewed-by: default avatarShawn Rutledge <shawn.rutledge@digia.com>
Reviewed-by: default avatarEike Ziller <eike.ziller@digia.com>
parent 8e18adc7
......@@ -82,7 +82,11 @@ void AndroidDeployStep::ctor()
//: AndroidDeployStep default display name
setDefaultDisplayName(tr("Deploy to Android device"));
m_deployAction = NoDeploy;
m_useLocalQtLibs = false;
if (QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target()->kit()))
if (qt->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0))
m_useLocalQtLibs = true;
}
bool AndroidDeployStep::init()
......@@ -197,22 +201,20 @@ void AndroidDeployStep::cleanLibsFinished()
void AndroidDeployStep::setDeployAction(AndroidDeployStep::AndroidDeployAction deploy)
{
m_deployAction = deploy;
AndroidManager::updateDeploymentSettings(target());
}
void AndroidDeployStep::setDeployQASIPackagePath(const QString &package)
{
m_QASIPackagePath = package;
m_deployAction = InstallQASI;
setDeployAction(InstallQASI);
}
void AndroidDeployStep::setUseLocalQtLibs(bool useLocal)
{
m_useLocalQtLibs = useLocal;
// ### Passes -1 for API level, which means it won't work with setups that require
// library selection based on API level. Use the old approach (command line argument)
// in these cases.
AndroidManager::setUseLocalLibs(target(), useLocal, -1);
AndroidManager::updateDeploymentSettings(target());
}
bool AndroidDeployStep::runCommand(QProcess *buildProc,
......
......@@ -62,6 +62,7 @@ namespace {
const QLatin1String AndroidLibsFileName("/res/values/libs.xml");
const QLatin1String AndroidStringsFileName("/res/values/strings.xml");
const QLatin1String AndroidDefaultPropertiesName("project.properties");
const QLatin1String AndroidLibraryPrefix("--Managed_by_Qt_Creator--");
QString cleanPackageName(QString packageName)
{
......@@ -373,7 +374,20 @@ QString AndroidManager::targetApplication(ProjectExplorer::Target *target)
return QString();
}
bool AndroidManager::setUseLocalLibs(ProjectExplorer::Target *target, bool useLocalLibs, int deviceAPILevel)
bool AndroidManager::bundleQt(ProjectExplorer::Target *target)
{
ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration();
AndroidRunConfiguration *androidRunConfiguration = qobject_cast<AndroidRunConfiguration *>(runConfiguration);
if (androidRunConfiguration != 0) {
AndroidDeployStep *deployStep = androidRunConfiguration->deployStep();
return deployStep->deployAction() == AndroidDeployStep::NoDeploy
&& deployStep->useLocalQtLibs();
}
return false;
}
bool AndroidManager::updateDeploymentSettings(ProjectExplorer::Target *target)
{
// For Qt 4, the "use local libs" options is handled by passing command line arguments to the
// app, so no need to alter the AndroidManifest.xml
......@@ -381,19 +395,32 @@ bool AndroidManager::setUseLocalLibs(ProjectExplorer::Target *target, bool useLo
if (baseQtVersion == 0 || baseQtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0))
return true;
ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration();
AndroidRunConfiguration *androidRunConfiguration = qobject_cast<AndroidRunConfiguration *>(runConfiguration);
if (androidRunConfiguration == 0)
return false;
AndroidDeployStep *deployStep = androidRunConfiguration->deployStep();
bool useLocalLibs = deployStep->useLocalQtLibs();
bool deployQtLibs = deployStep->deployAction() != AndroidDeployStep::NoDeploy;
bool bundleQtLibs = useLocalLibs && !deployQtLibs;
QDomDocument doc;
if (!openManifest(target, doc))
return false;
QDomElement metadataElem = doc.documentElement().firstChildElement(QLatin1String("application")).firstChildElement(QLatin1String("activity")).firstChildElement(QLatin1String("meta-data"));
// ### Passes -1 for API level, which means it won't work with setups that require
// library selection based on API level. Use the old approach (command line argument)
// in these cases. Hence the Qt version > 4 condition at the beginning of this function.
QString localLibs;
QString localJars;
QString staticInitClasses;
if (useLocalLibs) {
localLibs = loadLocalLibs(target, deviceAPILevel);
localJars = loadLocalJars(target, deviceAPILevel);
staticInitClasses = loadLocalJarsInitClasses(target, deviceAPILevel);
localLibs = loadLocalLibs(target, -1);
localJars = loadLocalJars(target, -1);
staticInitClasses = loadLocalJarsInitClasses(target, -1);
}
bool changedManifest = false;
......@@ -418,6 +445,11 @@ bool AndroidManager::setUseLocalLibs(ProjectExplorer::Target *target, bool useLo
metadataElem.setAttribute(QLatin1String("android:value"), staticInitClasses);
changedManifest = true;
}
} else if (metadataElem.attribute(QLatin1String("android:name")) == QLatin1String("android.app.bundle_local_qt_libs")) {
if (metadataElem.attribute(QLatin1String("android:value")).toInt() != bundleQtLibs) {
metadataElem.setAttribute(QLatin1String("android:value"), int(bundleQtLibs));
changedManifest = true;
}
}
metadataElem = metadataElem.nextSiblingElement(QLatin1String("meta-data"));
......@@ -653,14 +685,21 @@ QString AndroidManager::loadLocalLibs(ProjectExplorer::Target *target, int apiLe
return loadLocal(target, apiLevel, Lib);
}
QString AndroidManager::loadLocalBundledFiles(ProjectExplorer::Target *target, int apiLevel)
{
return loadLocal(target, apiLevel, BundledFile);
}
QString AndroidManager::loadLocalJars(ProjectExplorer::Target *target, int apiLevel)
{
return loadLocal(target, apiLevel, Jar);
ItemType type = bundleQt(target) ? BundledJar : Jar;
return loadLocal(target, apiLevel, type);
}
QString AndroidManager::loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel)
{
return loadLocal(target, apiLevel, Jar, QLatin1String("initClass"));
ItemType type = bundleQt(target) ? BundledJar : Jar;
return loadLocal(target, apiLevel, type, QLatin1String("initClass"));
}
QVector<AndroidManager::Library> AndroidManager::availableQtLibsWithDependencies(ProjectExplorer::Target *target)
......@@ -756,6 +795,16 @@ bool AndroidManager::setQtLibs(ProjectExplorer::Target *target, const QStringLis
return setLibsXml(target, libs, QLatin1String("qt_libs"));
}
bool AndroidManager::setBundledInAssets(ProjectExplorer::Target *target, const QStringList &fileList)
{
return setLibsXml(target, fileList, QLatin1String("bundled_in_assets"));
}
bool AndroidManager::setBundledInLib(ProjectExplorer::Target *target, const QStringList &fileList)
{
return setLibsXml(target, fileList, QLatin1String("bundled_in_lib"));
}
QStringList AndroidManager::availablePrebundledLibs(ProjectExplorer::Target *target)
{
QStringList libs;
......@@ -799,7 +848,9 @@ QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel,
QString itemType;
if (item == Lib)
itemType = QLatin1String("lib");
else
else if (item == BundledFile)
itemType = QLatin1String("bundled");
else // Jar or BundledJar
itemType = QLatin1String("jar");
QString localLibs;
......@@ -841,16 +892,18 @@ QString AndroidManager::loadLocal(ProjectExplorer::Target *target, int apiLevel,
if (libs.contains(element.attribute(QLatin1String("name")))) {
QDomElement libElement = element.firstChildElement(QLatin1String("depends")).firstChildElement(itemType);
while (!libElement.isNull()) {
if (libElement.hasAttribute(attribute)) {
QString dependencyLib = libElement.attribute(attribute).arg(apiLevel);
if (!dependencyLibs.contains(dependencyLib))
dependencyLibs << dependencyLib;
}
if (libElement.hasAttribute(QLatin1String("replaces"))) {
QString replacedLib = libElement.attribute(QLatin1String("replaces")).arg(apiLevel);
if (!replacedLibs.contains(replacedLib))
replacedLibs << replacedLib;
if (libElement.attribute(QLatin1String("bundling")).toInt() == (item == BundledJar ? 1 : 0)) {
if (libElement.hasAttribute(attribute)) {
QString dependencyLib = libElement.attribute(attribute).arg(apiLevel);
if (!dependencyLibs.contains(dependencyLib))
dependencyLibs << dependencyLib;
}
if (libElement.hasAttribute(QLatin1String("replaces"))) {
QString replacedLib = libElement.attribute(QLatin1String("replaces")).arg(apiLevel);
if (!replacedLibs.contains(replacedLib))
replacedLibs << replacedLib;
}
}
libElement = libElement.nextSiblingElement(itemType);
......@@ -1058,5 +1111,10 @@ QString AndroidManager::libGnuStl(const QString &arch, const QString &ndkToolCha
+ QLatin1String("/libgnustl_shared.so");
}
QString AndroidManager::libraryPrefix()
{
return AndroidLibraryPrefix;
}
} // namespace Internal
} // namespace Qt4ProjectManager
......@@ -85,7 +85,8 @@ public:
static bool setTargetApplication(ProjectExplorer::Target *target, const QString &name);
static QString targetApplicationPath(ProjectExplorer::Target *target);
static bool setUseLocalLibs(ProjectExplorer::Target *target, bool useLocalLibs, int deviceAPILevel);
static bool updateDeploymentSettings(ProjectExplorer::Target *target);
static bool bundleQt(ProjectExplorer::Target *target);
static QString targetSDK(ProjectExplorer::Target *target);
static bool setTargetSDK(ProjectExplorer::Target *target, const QString &sdk);
......@@ -103,9 +104,10 @@ public:
const QString &name = QString());
static Utils::FileName localLibsRulesFilePath(ProjectExplorer::Target *target);
static QString loadLocalLibs(ProjectExplorer::Target *target, int apiLevel);
static QString loadLocalJars(ProjectExplorer::Target *target, int apiLevel);
static QString loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel);
static QString loadLocalLibs(ProjectExplorer::Target *target, int apiLevel = -1);
static QString loadLocalJars(ProjectExplorer::Target *target, int apiLevel = -1);
static QString loadLocalBundledFiles(ProjectExplorer::Target *target, int apiLevel = -1);
static QString loadLocalJarsInitClasses(ProjectExplorer::Target *target, int apiLevel = -1);
class Library
{
......@@ -123,11 +125,17 @@ public:
static QStringList qtLibs(ProjectExplorer::Target *target);
static bool setQtLibs(ProjectExplorer::Target *target, const QStringList &libs);
static bool setBundledInLib(ProjectExplorer::Target *target,
const QStringList &fileList);
static bool setBundledInAssets(ProjectExplorer::Target *target,
const QStringList &fileList);
static QStringList availablePrebundledLibs(ProjectExplorer::Target *target);
static QStringList prebundledLibs(ProjectExplorer::Target *target);
static bool setPrebundledLibs(ProjectExplorer::Target *target, const QStringList &libs);
static QString libGnuStl(const QString &arch, const QString &ndkToolChainVersion);
static QString libraryPrefix();
private:
static void raiseError(const QString &reason);
......@@ -143,7 +151,9 @@ private:
enum ItemType
{
Lib,
Jar
Jar,
BundledFile,
BundledJar
};
static QString loadLocal(ProjectExplorer::Target *target, int apiLevel, ItemType item, const QString &attribute=QLatin1String("file"));
......
......@@ -45,6 +45,8 @@ QT_END_NAMESPACE
namespace Android {
namespace Internal {
class DeployItem;
class AndroidPackageCreationStep : public ProjectExplorer::BuildStep
{
Q_OBJECT
......@@ -82,6 +84,8 @@ private slots:
void showInGraphicalShell();
void setQtLibs(const QStringList &qtLibs);
void setPrebundledLibs(const QStringList &prebundledLibs);
void updateXmlForFiles(const QStringList &inLibList, const QStringList &inAssetsList);
void getBundleInformation();
signals:
void updateRequiredLibrariesModels();
......@@ -100,6 +104,12 @@ private:
void raiseError(const QString &shortMsg,
const QString &detailedMsg = QString());
QStringList collectRelativeFilePaths(const QString &parentPath);
void collectFiles(QList<DeployItem> *deployList, QList<DeployItem> *pluginsAndImports);
void removeManagedFilesFromPackage();
void copyFilesIntoPackage(const QList<DeployItem> &deployList);
void stripFiles(const QList<DeployItem> &deployList);
static const Core::Id CreatePackageId;
private:
......@@ -126,8 +136,13 @@ private:
Utils::FileName m_appPath;
Utils::FileName m_readElf;
QStringList m_qtLibs;
QStringList m_qtLibsWithDependencies;
QVector<AndroidManager::Library> m_availableQtLibs;
QStringList m_prebundledLibs;
QStringList m_bundledJars;
QStringList m_otherBundledFiles;
bool m_bundleQt;
};
} // namespace Internal
......
......@@ -387,24 +387,16 @@ void AndroidPackageCreationWidget::setQtLibs(QModelIndex, QModelIndex)
{
AndroidManager::setQtLibs(m_step->target(), m_qtLibsModel->checkedItems());
AndroidDeployStep * const deployStep = AndroidGlobal::buildStep<AndroidDeployStep>(m_step->target()->activeDeployConfiguration());
if (deployStep->useLocalQtLibs()) {
// ### Passes -1 for API level, which means it won't work with setups that require
// library selection based on API level. Use the old approach (command line argument)
// in these cases.
AndroidManager::setUseLocalLibs(m_step->target(), true, -1);
}
if (deployStep->useLocalQtLibs())
AndroidManager::updateDeploymentSettings(m_step->target());
}
void AndroidPackageCreationWidget::setPrebundledLibs(QModelIndex, QModelIndex)
{
AndroidManager::setPrebundledLibs(m_step->target(), m_prebundledLibs->checkedItems());
AndroidDeployStep * const deployStep = AndroidGlobal::buildStep<AndroidDeployStep>(m_step->target()->activeDeployConfiguration());
if (deployStep->useLocalQtLibs()) {
// ### Passes -1 for API level, which means it won't work with setups that require
// library selection based on API level. Use the old approach (command line argument)
// in these cases.
AndroidManager::setUseLocalLibs(m_step->target(), true, -1);
}
if (deployStep->useLocalQtLibs())
AndroidManager::updateDeploymentSettings(m_step->target());
}
void AndroidPackageCreationWidget::prebundledLibSelected(const QModelIndex &index)
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment