diff options
author | Daniel Teske <daniel.teske@digia.com> | 2013-09-17 18:24:57 +0200 |
---|---|---|
committer | Daniel Teske <daniel.teske@digia.com> | 2013-09-26 17:13:27 +0200 |
commit | 1262310798c699c13c2878c5eee66f94a6be8469 (patch) | |
tree | 18e7129681a0571dee91211e199669e50103c30c | |
parent | 455d597ac3cb6f1f6e1cc500c0a009df12cb4fcc (diff) | |
download | qt-creator-1262310798c699c13c2878c5eee66f94a6be8469.tar.gz |
Android: androiddeployqt support
Change-Id: I37d706b4e11c6e1353a8ee73378b7d080080678c
Reviewed-by: Eskil Abrahamsen Blomfeldt <eskil.abrahamsen-blomfeldt@digia.com>
30 files changed, 1876 insertions, 188 deletions
diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index afa6577723..3cb2e1de1e 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -42,7 +42,10 @@ HEADERS += \ androidmanifesteditor.h \ androidmanifesteditorwidget.h \ androidmanifestdocument.h \ - androiddevicedialog.h + androiddevicedialog.h \ + androiddeployqtstep.h \ + certificatesmodel.h \ + androiddeployqtwidget.h SOURCES += \ androidconfigurations.cpp \ @@ -79,7 +82,10 @@ SOURCES += \ androidmanifesteditor.cpp \ androidmanifesteditorwidget.cpp \ androidmanifestdocument.cpp \ - androiddevicedialog.cpp + androiddevicedialog.cpp \ + androiddeployqtstep.cpp \ + certificatesmodel.cpp \ + androiddeployqtwidget.cpp FORMS += \ androidsettingswidget.ui \ @@ -87,7 +93,8 @@ FORMS += \ androiddeploystepwidget.ui \ addnewavddialog.ui \ androidcreatekeystorecertificate.ui \ - androiddevicedialog.ui + androiddevicedialog.ui \ + androiddeployqtwidget.ui exists(../../shared/qbs/qbs.pro) { HEADERS += \ diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs index 8abc6e8110..13a4c91058 100644 --- a/src/plugins/android/android.qbs +++ b/src/plugins/android/android.qbs @@ -36,6 +36,8 @@ QtcPlugin { "androidcreatekeystorecertificate.cpp", "androidcreatekeystorecertificate.h", "androidcreatekeystorecertificate.ui", + "androiddeployqtstep.cpp", + "androiddeployqtstep.h", "androiddebugsupport.cpp", "androiddebugsupport.h", "androiddevicedialog.cpp", @@ -50,6 +52,9 @@ QtcPlugin { "androiddeploystepwidget.cpp", "androiddeploystepwidget.h", "androiddeploystepwidget.ui", + "androiddeployqtwidget.cpp", + "androiddeployqtwidget.h", + "androiddeployqtwidget.ui", "androiddevice.cpp", "androiddevice.h", "androiddevicefactory.cpp", @@ -103,6 +108,8 @@ QtcPlugin { "androidsettingswidget.ui", "androidtoolchain.cpp", "androidtoolchain.h", + "certificatesmodel.cpp", + "certificatesmodel.h", "javaparser.cpp", "javaparser.h", ] diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index e0a3ed0048..fa71d84f90 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -715,6 +715,13 @@ QStringList AndroidConfigurations::getAbis(const QString &device) const return result; } +QString AndroidConfigurations::highestAvailableAndroidPlatform() const +{ + if (m_availablePlatforms.isEmpty()) + return QString(); + return QLatin1String("android-") + QString::number(m_availablePlatforms.first()); +} + QString AndroidConfigurations::bestMatch(const QString &targetAPI) const { int target = targetAPI.mid(targetAPI.lastIndexOf(QLatin1Char('-')) + 1).toInt(); diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 0b89957a4b..a8d044a205 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -124,6 +124,7 @@ public: AndroidDeviceInfo showDeviceDialog(ProjectExplorer::Project *project, int apiLevel, const QString &abi); void setDefaultDevice(ProjectExplorer::Project *project, const QString &abi, const QString &serialNumber); // serial number or avd name QString defaultDevice(ProjectExplorer::Project *project, const QString &abi) const; // serial number or avd name + QString highestAvailableAndroidPlatform() const; public slots: void clearDefaultDevices(ProjectExplorer::Project *project); diff --git a/src/plugins/android/androiddeployconfiguration.cpp b/src/plugins/android/androiddeployconfiguration.cpp index 9c8305e3ab..ab1227d9c2 100644 --- a/src/plugins/android/androiddeployconfiguration.cpp +++ b/src/plugins/android/androiddeployconfiguration.cpp @@ -32,6 +32,7 @@ #include "androiddeploystep.h" #include "androidpackageinstallationstep.h" #include "androidpackagecreationstep.h" +#include "androiddeployqtstep.h" #include "androidmanager.h" #include <projectexplorer/buildsteplist.h> @@ -76,9 +77,15 @@ DeployConfiguration *AndroidDeployConfigurationFactory::create(Target *parent, c AndroidDeployConfiguration *dc = new AndroidDeployConfiguration(parent, id); if (!dc) return 0; - dc->stepList()->insertStep(0, new AndroidPackageInstallationStep(dc->stepList())); - dc->stepList()->insertStep(1, new AndroidPackageCreationStep(dc->stepList())); - dc->stepList()->insertStep(2, new AndroidDeployStep(dc->stepList())); + + if (id == ANDROID_DEPLOYCONFIGURATION_ID) { + dc->stepList()->insertStep(0, new AndroidPackageInstallationStep(AndroidPackageInstallationStep::ProjectDirectory, dc->stepList())); + dc->stepList()->insertStep(1, new AndroidPackageCreationStep(dc->stepList())); + dc->stepList()->insertStep(2, new AndroidDeployStep(dc->stepList())); + } else { + dc->stepList()->insertStep(0, new AndroidPackageInstallationStep(AndroidPackageInstallationStep::BuildDirectory, dc->stepList())); + dc->stepList()->insertStep(1, new AndroidDeployQtStep(dc->stepList())); + } return dc; } @@ -104,7 +111,7 @@ bool AndroidDeployConfigurationFactory::canClone(Target *parent, DeployConfigura { if (!AndroidManager::supportsAndroid(parent)) return false; - return source->id() == ANDROID_DEPLOYCONFIGURATION_ID; + return canCreate(parent, source->id()); } DeployConfiguration *AndroidDeployConfigurationFactory::clone(Target *parent, DeployConfiguration *source) @@ -128,16 +135,20 @@ QList<Core::Id> AndroidDeployConfigurationFactory::availableCreationIds(Target * if (!tc || tc->targetAbi().osFlavor() != Abi::AndroidLinuxFlavor) return ids; - if (QtSupport::QtKitInformation::qtVersion(parent->kit())->type() != QLatin1String(Constants::ANDROIDQT)) + QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(parent->kit()); + if (qt->type() != QLatin1String(Constants::ANDROIDQT)) return ids; - - ids << Core::Id(ANDROID_DEPLOYCONFIGURATION_ID); + if (qt->qtVersion() < QtSupport::QtVersionNumber(5, 2, 0)) + ids << Core::Id(ANDROID_DEPLOYCONFIGURATION_ID); + else + ids << Core::Id(ANDROID_DEPLOYCONFIGURATION2_ID); return ids; } QString AndroidDeployConfigurationFactory::displayNameForId(const Core::Id id) const { - if (id.name().startsWith(ANDROID_DC_PREFIX)) + if (id.name().startsWith(ANDROID_DC_PREFIX) + || id.name().startsWith(ANDROID_DC2_PREFIX)) return tr("Deploy on Android"); return QString(); } diff --git a/src/plugins/android/androiddeployconfiguration.h b/src/plugins/android/androiddeployconfiguration.h index e464415548..654c5843c8 100644 --- a/src/plugins/android/androiddeployconfiguration.h +++ b/src/plugins/android/androiddeployconfiguration.h @@ -38,6 +38,10 @@ namespace Internal { const char ANDROID_DEPLOYCONFIGURATION_ID[] = "Qt4ProjectManager.AndroidDeployConfiguration"; const char ANDROID_DC_PREFIX[] = "Qt4ProjectManager.AndroidDeployConfiguration."; +// Qt 5.2 has a new form of deployment +const char ANDROID_DEPLOYCONFIGURATION2_ID[] = "Qt4ProjectManager.AndroidDeployConfiguration2"; +const char ANDROID_DC2_PREFIX[] = "Qt4ProjectManager.AndroidDeployConfiguration2."; + class AndroidDeployConfiguration : public ProjectExplorer::DeployConfiguration { Q_OBJECT diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp new file mode 100644 index 0000000000..ea5d6eeac0 --- /dev/null +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -0,0 +1,554 @@ +/************************************************************************** +** +** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com> +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "androiddeployqtstep.h" +#include "androiddeployqtwidget.h" +#include "certificatesmodel.h" + +#include "javaparser.h" +#include "androidmanager.h" + +#include <utils/qtcassert.h> +#include <utils/qtcprocess.h> +#include <coreplugin/fileutils.h> +#include <coreplugin/icore.h> +#include <coreplugin/messagemanager.h> +#include <projectexplorer/buildsteplist.h> +#include <projectexplorer/projectexplorerconstants.h> +#include <projectexplorer/target.h> +#include <projectexplorer/project.h> +#include <qtsupport/qtkitinformation.h> +#include <qt4projectmanager/qt4buildconfiguration.h> +#include <qt4projectmanager/qt4project.h> +#include <qt4projectmanager/qt4nodes.h> +#include <QInputDialog> +#include <QMessageBox> + +using namespace Android; +using namespace Android::Internal; + +const QLatin1String DeployActionKey("Qt4ProjectManager.AndroidDeployQtStep.DeployQtAction"); +const QLatin1String KeystoreLocationKey("KeystoreLocation"); +const QLatin1String SignPackageKey("SignPackage"); +const QLatin1String BuildTargetSdkKey("BuildTargetSdk"); +const QLatin1String VerboseOutputKey("VerboseOutput"); +const QLatin1String InputFile("InputFile"); +const Core::Id AndroidDeployQtStep::Id("Qt4ProjectManager.AndroidDeployQtStep"); + +////////////////// +// AndroidDeployQtStepFactory +///////////////// + +AndroidDeployQtStepFactory::AndroidDeployQtStepFactory(QObject *parent) + : IBuildStepFactory(parent) +{ +} + +QList<Core::Id> AndroidDeployQtStepFactory::availableCreationIds(ProjectExplorer::BuildStepList *parent) const +{ + if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) + return QList<Core::Id>(); + if (!AndroidManager::supportsAndroid(parent->target())) + return QList<Core::Id>(); + if (parent->contains(AndroidDeployQtStep::Id)) + return QList<Core::Id>(); + return QList<Core::Id>() << AndroidDeployQtStep::Id; +} + +QString AndroidDeployQtStepFactory::displayNameForId(const Core::Id id) const +{ + if (id == AndroidDeployQtStep::Id) + return tr("Deploy to Android device or emulator"); + return QString(); +} + +bool AndroidDeployQtStepFactory::canCreate(ProjectExplorer::BuildStepList *parent, const Core::Id id) const +{ + return availableCreationIds(parent).contains(id); +} + +ProjectExplorer::BuildStep *AndroidDeployQtStepFactory::create(ProjectExplorer::BuildStepList *parent, const Core::Id id) +{ + Q_ASSERT(canCreate(parent, id)); + Q_UNUSED(id); + return new AndroidDeployQtStep(parent); +} + +bool AndroidDeployQtStepFactory::canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const +{ + return canCreate(parent, ProjectExplorer::idFromMap(map)); +} + +ProjectExplorer::BuildStep *AndroidDeployQtStepFactory::restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) +{ + Q_ASSERT(canRestore(parent, map)); + AndroidDeployQtStep * const step = new AndroidDeployQtStep(parent); + if (!step->fromMap(map)) { + delete step; + return 0; + } + return step; +} + +bool AndroidDeployQtStepFactory::canClone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) const +{ + return canCreate(parent, product->id()); +} + +ProjectExplorer::BuildStep *AndroidDeployQtStepFactory::clone(ProjectExplorer::BuildStepList *parent, ProjectExplorer::BuildStep *product) +{ + Q_ASSERT(canClone(parent, product)); + return new AndroidDeployQtStep(parent, static_cast<AndroidDeployQtStep *>(product)); +} + +////////////////// +// AndroidDeployQtStep +///////////////// + +AndroidDeployQtStep::AndroidDeployQtStep(ProjectExplorer::BuildStepList *parent) + : ProjectExplorer::AbstractProcessStep(parent, Id) +{ + ctor(); +} + +AndroidDeployQtStep::AndroidDeployQtStep(ProjectExplorer::BuildStepList *parent, + AndroidDeployQtStep *other) + : ProjectExplorer::AbstractProcessStep(parent, other) +{ + ctor(); +} + +void AndroidDeployQtStep::ctor() +{ + //: AndroidDeployQtStep default display name + setDefaultDisplayName(tr("Deploy to Android device")); + m_deployAction = BundleLibrariesDeployment; + m_signPackage = false; + m_openPackageLocation = false; + m_verbose = false; + + // will be overwriten by settings if the user choose something different + m_buildTargetSdk = AndroidConfigurations::instance().highestAvailableAndroidPlatform(); + + connect(project(), SIGNAL(proFilesEvaluated()), + this, SLOT(updateInputFile())); +} + +bool AndroidDeployQtStep::init() +{ + if (AndroidManager::checkForQt51Files(project()->projectDirectory())) + emit addOutput(tr("Found old android folder in source directory. Qt 5.2 does not use that folder by default."), ErrorOutput); + + m_targetArch = AndroidManager::targetArch(target()); + m_deviceAPILevel = AndroidManager::minimumSDK(target()); + AndroidDeviceInfo info = AndroidConfigurations::instance().showDeviceDialog(project(), m_deviceAPILevel, m_targetArch); + if (info.serialNumber.isEmpty()) // aborted + return false; + + if (info.type == AndroidDeviceInfo::Emulator) { + m_avdName = info.serialNumber; + m_serialNumber.clear(); + m_deviceAPILevel = info.sdk; + } else { + m_avdName.clear(); + m_serialNumber = info.serialNumber; + } + + Qt4ProjectManager::Qt4BuildConfiguration *bc + = static_cast<Qt4ProjectManager::Qt4BuildConfiguration *>(target()->activeBuildConfiguration()); + + if (m_signPackage) { + // check keystore and certificate passwords + while (!AndroidManager::checkKeystorePassword(m_keystorePath.toString(), m_keystorePasswd)) { + if (!keystorePassword()) + return false; // user canceled + } + + while (!AndroidManager::checkCertificatePassword(m_keystorePath.toString(), m_keystorePasswd, m_certificateAlias, m_certificatePasswd)) { + if (!certificatePassword()) + return false; // user canceled + } + + + if ((bc->qmakeBuildConfiguration() & QtSupport::BaseQtVersion::DebugBuild)) + emit addOutput(tr("Warning: Signing a debug package."), BuildStep::ErrorMessageOutput); + } + + QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(target()->kit()); + if (!version) + return false; + + ProjectExplorer::Project *project = target()->project(); + JavaParser *parser = new JavaParser; + parser->setProjectFileList(project->files(ProjectExplorer::Project::AllFiles)); + setOutputParser(parser); + + QString command = version->qmakeProperty("QT_HOST_BINS"); + if (!command.endsWith(QLatin1Char('/'))) + command += QLatin1Char('/'); + command += QLatin1String("androiddeployqt"); + if (Utils::HostOsInfo::isWindowsHost()) + command += QLatin1String(".exe"); + + QString deploymentMethod; + if (m_deployAction == MinistroDeployment) + deploymentMethod = QLatin1String("ministro"); + else if (m_deployAction == DebugDeployment) + deploymentMethod = QLatin1String("debug"); + else if (m_deployAction == BundleLibrariesDeployment) + deploymentMethod = QLatin1String("bundled"); + + QString outputDir = bc->buildDirectory().appendPath(QLatin1String("android")).toString(); + + QStringList arguments; + arguments << QLatin1String("--input") + << m_inputFile + << QLatin1String("--output") + << outputDir + << QLatin1String("--deployment") + << deploymentMethod + << QLatin1String("--install") + << QLatin1String("--ant") + << AndroidConfigurations::instance().antToolPath().toString() + << QLatin1String("--android-platform") + << m_buildTargetSdk + << QLatin1String("--jdk") + << AndroidConfigurations::instance().openJDKPath().toString(); + + if (m_verbose) + arguments << QLatin1String("--verbose"); + if (m_avdName.isEmpty()) + arguments << QLatin1String("--device") + << info.serialNumber; + + if (m_signPackage) { + arguments << QLatin1String("--sign") + << m_keystorePath.toString() + << m_certificateAlias + << QLatin1String("--storepass") + << m_keystorePasswd; + if (!m_certificatePasswd.isEmpty()) + arguments << QLatin1String("--keypass") + << m_certificatePasswd; + } + + ProjectExplorer::ProcessParameters *pp = processParameters(); + pp->setMacroExpander(bc->macroExpander()); + pp->setWorkingDirectory(bc->buildDirectory().toString()); + Utils::Environment env = bc->environment(); + // Force output to english for the parsers. Do this here and not in the toolchain's + // addToEnvironment() to not screw up the users run environment. + env.set(QLatin1String("LC_ALL"), QLatin1String("C")); + pp->setEnvironment(env); + pp->setCommand(command); + pp->setArguments(Utils::QtcProcess::joinArgs(arguments)); + pp->resolveAll(); + + m_openPackageLocationForRun = m_openPackageLocation; + m_apkPath = AndroidManager::apkPath(target(), m_signPackage ? AndroidManager::ReleaseBuildSigned + : AndroidManager::DebugBuild).toString(); + m_buildDirectory = bc->buildDirectory().toString(); + + bool result = AbstractProcessStep::init(); + if (!result) + return false; + + AndroidConfigurations::instance().startAVDAsync(m_avdName); + return true; +} + +void AndroidDeployQtStep::run(QFutureInterface<bool> &fi) +{ + if (!m_avdName.isEmpty()) { + QString serialNumber = AndroidConfigurations::instance().waitForAvd(m_deviceAPILevel, m_targetArch); + if (serialNumber.isEmpty()) { + fi.reportResult(false); + return; + } + m_serialNumber = serialNumber; + QString args = processParameters()->arguments(); + Utils::QtcProcess::addArg(&args, QLatin1String("--device")); + Utils::QtcProcess::addArg(&args, serialNumber); + processParameters()->setArguments(args); + } + + AbstractProcessStep::run(fi); + + emit addOutput(tr("Pulling files necessary for debugging."), MessageOutput); + runCommand(AndroidConfigurations::instance().adbToolPath().toString(), + AndroidDeviceInfo::adbSelector(m_serialNumber) + << QLatin1String("pull") << QLatin1String("/system/bin/app_process") + << QString::fromLatin1("%1/app_process").arg(m_buildDirectory)); + runCommand(AndroidConfigurations::instance().adbToolPath().toString(), + AndroidDeviceInfo::adbSelector(m_serialNumber) << QLatin1String("pull") + << QLatin1String("/system/lib/libc.so") + << QString::fromLatin1("%1/libc.so").arg(m_buildDirectory)); +} + +void AndroidDeployQtStep::runCommand(const QString &program, const QStringList &arguments) +{ + QProcess buildProc; + emit addOutput(tr("Package deploy: Running command '%1 %2'.").arg(program).arg(arguments.join(QLatin1String(" "))), BuildStep::MessageOutput); + buildProc.start(program, arguments); + if (!buildProc.waitForStarted()) { + emit addOutput(tr("Packaging error: Could not start command '%1 %2'. Reason: %3") + .arg(program).arg(arguments.join(QLatin1String(" "))).arg(buildProc.errorString()), BuildStep::ErrorMessageOutput); + return; + } + if (!buildProc.waitForFinished(2 * 60 * 1000) + || buildProc.error() != QProcess::UnknownError + || buildProc.exitCode() != 0) { + QString mainMessage = tr("Packaging Error: Command '%1 %2' failed.") + .arg(program).arg(arguments.join(QLatin1String(" "))); + if (buildProc.error() != QProcess::UnknownError) + mainMessage += tr(" Reason: %1").arg(buildProc.errorString()); + else + mainMessage += tr("Exit code: %1").arg(buildProc.exitCode()); + emit addOutput(mainMessage, BuildStep::ErrorMessageOutput); + } +} + +void AndroidDeployQtStep::updateInputFile() +{ + Qt4ProjectManager::Qt4Project *pro = static_cast<Qt4ProjectManager::Qt4Project *>(project()); + QList<Qt4ProjectManager::Qt4ProFileNode *> nodes = pro->applicationProFiles(); + + QStringList inputFiles; + foreach (Qt4ProjectManager::Qt4ProFileNode *node, nodes) + inputFiles << node->singleVariableValue(Qt4ProjectManager::AndroidDeploySettingsFile); + + if (!inputFiles.contains(m_inputFile)) + m_inputFile.clear(); + + if (m_inputFile.isEmpty()) { + // not yet selected one or no longer exists + if (!inputFiles.isEmpty()) + m_inputFile = inputFiles.first(); + } + + emit inputFileChanged(); +} + +void AndroidDeployQtStep::showInGraphicalShell() +{ + Core::FileUtils::showInGraphicalShell(Core::ICore::instance()->mainWindow(), m_apkPath); +} + +ProjectExplorer::BuildStepConfigWidget *AndroidDeployQtStep::createConfigWidget() +{ + return new AndroidDeployQtWidget(this); +} + +void AndroidDeployQtStep::processFinished(int exitCode, QProcess::ExitStatus status) +{ + AbstractProcessStep::processFinished(exitCode, status); + if (m_openPackageLocationForRun) + QMetaObject::invokeMethod(this, "showInGraphicalShell", Qt::QueuedConnection); +} + +bool AndroidDeployQtStep::fromMap(const QVariantMap &map) +{ + m_deployAction = AndroidDeployQtAction(map.value(QLatin1String(DeployActionKey), + BundleLibrariesDeployment).toInt()); + m_keystorePath = Utils::FileName::fromString(map.value(KeystoreLocationKey).toString()); + m_signPackage = map.value(SignPackageKey).toBool(); + m_buildTargetSdk = map.value(BuildTargetSdkKey).toString(); + m_verbose = map.value(VerboseOutputKey).toBool(); + m_inputFile = map.value(InputFile).toString(); + return ProjectExplorer::BuildStep::fromMap(map); +} + +QVariantMap AndroidDeployQtStep::toMap() const +{ + QVariantMap map = ProjectExplorer::BuildStep::toMap(); + map.insert(QLatin1String(DeployActionKey), m_deployAction); + map.insert(KeystoreLocationKey, m_keystorePath.toString()); + map.insert(SignPackageKey, m_signPackage); + map.insert(BuildTargetSdkKey, m_buildTargetSdk); + map.insert(VerboseOutputKey, m_verbose); + map.insert(InputFile, m_inputFile); + return map; +} + +void AndroidDeployQtStep::setBuildTargetSdk(const QString &sdk) +{ + m_buildTargetSdk = sdk; +} + +QString AndroidDeployQtStep::buildTargetSdk() const +{ + return m_buildTargetSdk; +} + +Utils::FileName AndroidDeployQtStep::keystorePath() +{ + return m_keystorePath; +} + +AndroidDeployQtStep::AndroidDeployQtAction AndroidDeployQtStep::deployAction() const +{ + return m_deployAction; +} + +void AndroidDeployQtStep::setDeployAction(AndroidDeployQtStep::AndroidDeployQtAction deploy) +{ + m_deployAction = deploy; +} + +void AndroidDeployQtStep::setKeystorePath(const Utils::FileName &path) +{ + m_keystorePath = path; + m_certificatePasswd.clear(); + m_keystorePasswd.clear(); +} + +void AndroidDeployQtStep::setKeystorePassword(const QString &pwd) +{ + m_keystorePasswd = pwd; +} + +void AndroidDeployQtStep::setCertificateAlias(const QString &alias) +{ + m_certificateAlias = alias; +} + +void AndroidDeployQtStep::setCertificatePassword(const QString &pwd) +{ + m_certificatePasswd = pwd; +} + +bool AndroidDeployQtStep::signPackage() const +{ + return m_signPackage; +} + +void AndroidDeployQtStep::setSignPackage(bool b) +{ + m_signPackage = b; +} + +QString AndroidDeployQtStep::deviceSerialNumber() +{ + return m_serialNumber; +} + +bool AndroidDeployQtStep::openPackageLocation() const +{ + return m_openPackageLocation; +} + +void AndroidDeployQtStep::setOpenPackageLocation(bool open) +{ + m_openPackageLocation = open; +} + +void AndroidDeployQtStep::setVerboseOutput(bool verbose) +{ + m_verbose = verbose; +} + +QString AndroidDeployQtStep::inputFile() const +{ + return m_inputFile; +} + +void AndroidDeployQtStep::setInputFile(const QString &file) +{ + m_inputFile = file; +} + +bool AndroidDeployQtStep::verboseOutput() const +{ + return m_verbose; +} + +// Note this functions is duplicated between AndroidDeployStep and AndroidDeployQtStep +// since it does modify the stored password in AndroidDeployQtStep it's not easily +// extractable. The situation will clean itself up once AndroidDeployStep is no longer +// necessary +QAbstractItemModel *AndroidDeployQtStep::keystoreCertificates() +{ + QString rawCerts; + QProcess keytoolProc; + while (!rawCerts.length() || !m_keystorePasswd.length()) { + QStringList params; + params << QLatin1String("-list") << QLatin1String("-v") << QLatin1String("-keystore") << m_keystorePath.toUserOutput() << QLatin1String("-storepass"); + if (!m_keystorePasswd.length()) + keystorePassword(); + if (!m_keystorePasswd.length()) + return 0; + params << m_keystorePasswd; + Utils::Environment env = Utils::Environment::systemEnvironment(); + env.set(QLatin1String("LANG"), QLatin1String("C")); + keytoolProc.setProcessEnvironment(env.toProcessEnvironment()); + keytoolProc.start(AndroidConfigurations::instance().keytoolPath().toString(), params); + if (!keytoolProc.waitForStarted() || !keytoolProc.waitForFinished()) { + QMessageBox::critical(0, tr("Error"), + tr("Failed to run keytool")); + return 0; + } + + if (keytoolProc.exitCode()) { + QMessageBox::critical(0, tr("Error"), + tr("Invalid password")); + m_keystorePasswd.clear(); + } + rawCerts = QString::fromLatin1(keytoolProc.readAllStandardOutput()); + } + return new CertificatesModel(rawCerts, this); +} + +bool AndroidDeployQtStep::keystorePassword() +{ + m_keystorePasswd.clear(); + bool ok; + QString text = QInputDialog::getText(0, tr("Keystore"), + tr("Keystore password:"), QLineEdit::Password, + QString(), &ok); + if (ok && !text.isEmpty()) { + m_keystorePasswd = text; + return true; + } + return false; +} + +bool AndroidDeployQtStep::certificatePassword() +{ + m_certificatePasswd.clear(); + bool ok; + QString text = QInputDialog::getText(0, tr("Certificate"), + tr("Certificate password (%1):").arg(m_certificateAlias), QLineEdit::Password, + QString(), &ok); + if (ok && !text.isEmpty()) { + m_certificatePasswd = text; + return true; + } + return false; +} diff --git a/src/plugins/android/androiddeployqtstep.h b/src/plugins/android/androiddeployqtstep.h new file mode 100644 index 0000000000..d51a46e60e --- /dev/null +++ b/src/plugins/android/androiddeployqtstep.h @@ -0,0 +1,160 @@ +/************************************************************************** +** +** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com> +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef ANDROIDDEPLOYQTSTEP_H +#define ANDROIDDEPLOYQTSTEP_H + +#include "androidconfigurations.h" + +#include <projectexplorer/abstractprocessstep.h> +#include <qtsupport/baseqtversion.h> + +QT_BEGIN_NAMESPACE +class QAbstractItemModel; +QT_END_NAMESPACE + +namespace Android { +namespace Internal { + +class AndroidDeployQtStepFactory : public ProjectExplorer::IBuildStepFactory +{ + Q_OBJECT +public: + explicit AndroidDeployQtStepFactory(QObject *parent = 0); + + QList<Core::Id> availableCreationIds(ProjectExplorer::BuildStepList *parent) const; + QString displayNameForId(const Core::Id id) const; + + bool canCreate(ProjectExplorer::BuildStepList *parent, + const Core::Id id) const; + ProjectExplorer::BuildStep *create(ProjectExplorer::BuildStepList *parent, const Core::Id id); + + bool canRestore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map) const; + ProjectExplorer::BuildStep *restore(ProjectExplorer::BuildStepList *parent, const QVariantMap &map); + + bool canClone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product) const; + ProjectExplorer::BuildStep *clone(ProjectExplorer::BuildStepList *parent, + ProjectExplorer::BuildStep *product); +}; + +class AndroidDeployQtStep : public ProjectExplorer::AbstractProcessStep +{ + Q_OBJECT + friend class AndroidDeployQtStepFactory; +public: + AndroidDeployQtStep(ProjectExplorer::BuildStepList *bc); + + enum AndroidDeployQtAction + { + MinistroDeployment, // use ministro + DebugDeployment, + BundleLibrariesDeployment + }; + + bool fromMap(const QVariantMap &map); + QVariantMap toMap() const; + + AndroidDeployQtStep::AndroidDeployQtAction deployAction() const; + QString deviceSerialNumber(); + + void setBuildTargetSdk(const QString &sdk); + QString buildTargetSdk() const; + + // signing + Utils::FileName keystorePath(); + void setKeystorePath(const Utils::FileName &path); + void setKeystorePassword(const QString &pwd); + void setCertificateAlias(const QString &alias); + void setCertificatePassword(const QString &pwd); + + QAbstractItemModel *keystoreCertificates(); + bool signPackage() const; + void setSignPackage(bool b); + + bool openPackageLocation() const; + void setOpenPackageLocation(bool open); + bool verboseOutput() const; + void setVerboseOutput(bool verbose); + + QString inputFile() const; + void setInputFile(const QString &file); + +signals: + // also on purpose emitted if the possible values of this changed + void inputFileChanged(); + +public slots: + void setDeployAction(AndroidDeployQtAction deploy); // slot? + +private slots: + void showInGraphicalShell(); + + void updateInputFile(); +private: + AndroidDeployQtStep(ProjectExplorer::BuildStepList *bc, + AndroidDeployQtStep *other); + void ctor(); + bool keystorePassword(); + bool certificatePassword(); + void runCommand(const QString &program, const QStringList &arguments); + + bool init(); + void run(QFutureInterface<bool> &fi); + ProjectExplorer::BuildStepConfigWidget *createConfigWidget(); + bool immutable() const { return true; } + void processFinished(int exitCode, QProcess::ExitStatus status); + + QString m_buildTargetSdk; + QString m_serialNumber; + AndroidDeployQtAction m_deployAction; + bool m_signPackage; + bool m_verbose; + bool m_openPackageLocation; + bool m_openPackageLocationForRun; + QString m_buildDirectory; + + Utils::FileName m_keystorePath; + QString m_keystorePasswd; + QString m_certificateAlias; + QString m_certificatePasswd; + QString m_avdName; + QString m_apkPath; + QString m_targetArch; + QString m_inputFile; + int m_deviceAPILevel; + + static const Core::Id Id; +}; + +} +} + +#endif // ANDROIDDEPLOYQTSTEP_H diff --git a/src/plugins/android/androiddeployqtwidget.cpp b/src/plugins/android/androiddeployqtwidget.cpp new file mode 100644 index 0000000000..d6234b9932 --- /dev/null +++ b/src/plugins/android/androiddeployqtwidget.cpp @@ -0,0 +1,314 @@ +/************************************************************************** +** +** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com> +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "androiddeployqtwidget.h" +#include "ui_androiddeployqtwidget.h" + +#include "androidcreatekeystorecertificate.h" +#include "androiddeployqtstep.h" +#include "androidmanager.h" + +#include <projectexplorer/target.h> +#include <qt4projectmanager/qt4buildconfiguration.h> +#include <qt4projectmanager/qt4project.h> +#include <qt4projectmanager/qt4nodes.h> + +#include <QFileDialog> + +#include <algorithm> + + +using namespace Android; +using namespace Internal; + +AndroidDeployQtWidget::AndroidDeployQtWidget(AndroidDeployQtStep *step) + : ProjectExplorer::BuildStepConfigWidget(), + m_ui(new Ui::AndroidDeployQtWidget), + m_step(step), + m_currentBuildConfiguration(0), + m_ignoreChange(false) +{ + m_ui->setupUi(this); + + // Target sdk combobox + int minApiLevel = 9; + QStringList targets = AndroidConfigurations::instance().sdkTargets(minApiLevel); + m_ui->targetSDKComboBox->addItems(targets); + m_ui->targetSDKComboBox->setCurrentIndex(targets.indexOf(step->buildTargetSdk())); + + // deployment option + switch (m_step->deployAction()) { + case AndroidDeployQtStep::MinistroDeployment: + m_ui->ministroOption->setChecked(true); + break; + case AndroidDeployQtStep::DebugDeployment: + m_ui->temporaryQtOption->setChecked(true); + break; + case AndroidDeployQtStep::BundleLibrariesDeployment: + m_ui->bundleQtOption->setChecked(true); + break; + default: + // can't happen + break; + } + + // signing + m_ui->KeystoreLocationLineEdit->setText(m_step->keystorePath().toUserOutput()); + m_ui->signingDebugWarningIcon->hide(); + m_ui->signingDebugWarningLabel->hide(); + + m_ui->verboseOutputCheckBox->setChecked(m_step->verboseOutput()); + m_ui->openPackageLocationCheckBox->setChecked(m_step->openPackageLocation()); + + bool oldFiles = AndroidManager::checkForQt51Files(m_step->project()->projectDirectory()); + m_ui->oldFilesWarningIcon->setVisible(oldFiles); + m_ui->oldFilesWarningLabel->setVisible(oldFiles); + + // target sdk + connect(m_ui->targetSDKComboBox, SIGNAL(activated(QString)), SLOT(setTargetSdk(QString))); + + // deployment options + connect(m_ui->ministroOption, SIGNAL(clicked()), SLOT(setMinistro())); + connect(m_ui->temporaryQtOption, SIGNAL(clicked()), SLOT(setDeployLocalQtLibs())); + connect(m_ui->bundleQtOption, SIGNAL(clicked()), SLOT(setBundleQtLibs())); + + connect(m_ui->installMinistroButton, SIGNAL(clicked()), SLOT(installMinistro())); + connect(m_ui->cleanLibsPushButton, SIGNAL(clicked()), SLOT(cleanLibsOnDevice())); + connect(m_ui->resetDefaultDevices, SIGNAL(clicked()), SLOT(resetDefaultDevices())); + connect(m_ui->openPackageLocationCheckBox, SIGNAL(toggled(bool)), + this, SLOT(openPackageLocationCheckBoxToggled(bool))); + connect(m_ui->verboseOutputCheckBox, SIGNAL(toggled(bool)), + this, SLOT(verboseOutputCheckBoxToggled(bool))); + + //signing + connect(m_ui->signPackageCheckBox, SIGNAL(toggled(bool)), + this, SLOT(signPackageCheckBoxToggled(bool))); + connect(m_ui->KeystoreCreatePushButton, SIGNAL(clicked()), + this, SLOT(createKeyStore())); + connect(m_ui->KeystoreLocationPushButton, SIGNAL(clicked()), + this, SLOT(browseKeyStore())); + connect(m_ui->certificatesAliasComboBox, SIGNAL(activated(QString)), + this, SLOT(certificatesAliasComboBoxActivated(QString))); + connect(m_ui->certificatesAliasComboBox, SIGNAL(currentIndexChanged(QString)), + this, SLOT(certificatesAliasComboBoxCurrentIndexChanged(QString))); + + activeBuildConfigurationChanged(); + connect(m_step->target(), SIGNAL(activeBuildConfigurationChanged(ProjectExplorer::BuildConfiguration*)), + this, SLOT(activeBuildConfigurationChanged())); + + connect(m_ui->inputFileComboBox, SIGNAL(currentIndexChanged(int)), + this, SLOT(inputFileComboBoxIndexChanged())); + + updateInputFileUi(); + connect(m_step, SIGNAL(inputFileChanged()), + this, SLOT(updateInputFileUi())); +} + +AndroidDeployQtWidget::~AndroidDeployQtWidget() +{ + delete m_ui; +} + +void AndroidDeployQtWidget::updateInputFileUi() +{ + Qt4ProjectManager::Qt4Project *project + = static_cast<Qt4ProjectManager::Qt4Project *>(m_step->project()); + QList<Qt4ProjectManager::Qt4ProFileNode *> nodes = project->applicationProFiles(); + int size = nodes.size(); + if (size == 0 || size == 1) { + // there's nothing to select, e.g. before parsing + m_ui->inputFileLabel->setVisible(false); + m_ui->inputFileComboBox->setVisible(false); + } else { + m_ignoreChange = true; + m_ui->inputFileLabel->setVisible(true); + m_ui->inputFileComboBox->setVisible(true); + + m_ui->inputFileComboBox->clear(); + foreach (Qt4ProjectManager::Qt4ProFileNode *node, nodes) { + QString file = node->singleVariableValue(Qt4ProjectManager::AndroidDeploySettingsFile); + m_ui->inputFileComboBox->addItem(node->displayName(), file); + } + + int index = m_ui->inputFileComboBox->findData(m_step->inputFile()); + m_ui->inputFileComboBox->setCurrentIndex(index); + m_ignoreChange = false; + } +} + +void AndroidDeployQtWidget::inputFileComboBoxIndexChanged() +{ + if (m_ignoreChange) + return; + QString text = m_ui->inputFileComboBox->currentData().toString(); + m_step->setInputFile(text); +} + +QString AndroidDeployQtWidget::displayName() const +{ + return tr("<b>Deploy configurations</b>"); +} + +QString AndroidDeployQtWidget::summaryText() const +{ + return displayName(); +} + +void AndroidDeployQtWidget::setTargetSdk(const QString &sdk) +{ + m_step->setBuildTargetSdk(sdk); +} + +void AndroidDeployQtWidget::setMinistro() +{ + m_step->setDeployAction(AndroidDeployQtStep::MinistroDeployment); +} + +void AndroidDeployQtWidget::setDeployLocalQtLibs() +{ + m_step->setDeployAction(AndroidDeployQtStep::DebugDeployment); +} + +void AndroidDeployQtWidget::setBundleQtLibs() +{ + m_step->setDeployAction(AndroidDeployQtStep::BundleLibrariesDeployment); +} + +void AndroidDeployQtWidget::installMinistro() +{ + QString packagePath = + QFileDialog::getOpenFileName(this, tr("Qt Android Smart Installer"), + QDir::homePath(), tr("Android package (*.apk)")); + if (!packagePath.isEmpty()) + AndroidManager::installQASIPackage(m_step->target(), packagePath); +} + +void AndroidDeployQtWidget::cleanLibsOnDevice() +{ + AndroidManager::cleanLibsOnDevice(m_step->target()); +} + +void AndroidDeployQtWidget::resetDefaultDevices() +{ + AndroidConfigurations::instance().clearDefaultDevices(m_step->project()); +} + +void AndroidDeployQtWidget::signPackageCheckBoxToggled(bool checked) +{ + m_ui->certificatesAliasComboBox->setEnabled(checked); + m_step->setSignPackage(checked); + updateSigningWarning(); + if (!checked) + return; + if (!m_step->keystorePath().isEmpty()) + setCertificates(); +} + +void AndroidDeployQtWidget::createKeyStore() +{ + AndroidCreateKeystoreCertificate d; + if (d.exec() != QDialog::Accepted) + return; + m_ui->KeystoreLocationLineEdit->setText(d.keystoreFilePath().toUserOutput()); + m_step->setKeystorePath(d.keystoreFilePath()); + m_step->setKeystorePassword(d.keystorePassword()); + m_step->setCertificateAlias(d.certificateAlias()); + m_step->setCertificatePassword(d.certificatePassword()); + setCertificates(); +} + +void AndroidDeployQtWidget::setCertificates() +{ + QAbstractItemModel *certificates = m_step->keystoreCertificates(); + m_ui->signPackageCheckBox->setChecked(certificates); + m_ui->certificatesAliasComboBox->setModel(certificates); +} + +void AndroidDeployQtWidget::browseKeyStore() +{ + Utils::FileName keystorePath = m_step->keystorePath(); + if (keystorePath.isEmpty()) + keystorePath = Utils::FileName::fromString(QDir::homePath()); + Utils::FileName file = Utils::FileName::fromString(QFileDialog::getOpenFileName(this, tr("Select keystore file"), keystorePath.toString(), tr("Keystore files (*.keystore *.jks)"))); + if (file.isEmpty()) + return; + m_ui->KeystoreLocationLineEdit->setText(file.toUserOutput()); + m_step->setKeystorePath(file); + m_ui->signPackageCheckBox->setChecked(false); +} + +void AndroidDeployQtWidget::certificatesAliasComboBoxActivated(const QString &alias) +{ + if (alias.length()) + m_step->setCertificateAlias(alias); +} + +void AndroidDeployQtWidget::certificatesAliasComboBoxCurrentIndexChanged(const QString &alias) +{ + if (alias.length()) + m_step->setCertificateAlias(alias); +} + +void AndroidDeployQtWidget::openPackageLocationCheckBoxToggled(bool checked) +{ + m_step->setOpenPackageLocation(checked); +} + +void AndroidDeployQtWidget::verboseOutputCheckBoxToggled(bool checked) +{ + m_step->setVerboseOutput(checked); +} + +void AndroidDeployQtWidget::activeBuildConfigurationChanged() +{ + if (m_currentBuildConfiguration) + disconnect(m_currentBuildConfiguration, SIGNAL(qmakeBuildConfigurationChanged()), + this, SLOT(updateSigningWarning())); + updateSigningWarning(); + Qt4ProjectManager::Qt4BuildConfiguration *bc + = qobject_cast<Qt4ProjectManager::Qt4BuildConfiguration *>(m_step->target()->activeBuildConfiguration()); + m_currentBuildConfiguration = bc; + if (bc) + connect(bc, SIGNAL(qmakeBuildConfigurationChanged()), this, SLOT(updateSigningWarning())); + m_currentBuildConfiguration = bc; +} + +void AndroidDeployQtWidget::updateSigningWarning() +{ + Qt4ProjectManager::Qt4BuildConfiguration *bc = qobject_cast<Qt4ProjectManager::Qt4BuildConfiguration *>(m_step->target()->activeBuildConfiguration()); + bool debug = bc && (bc->qmakeBuildConfiguration() & QtSupport::BaseQtVersion::DebugBuild); + if (m_step->signPackage() && debug) { + m_ui->signingDebugWarningIcon->setVisible(true); + m_ui->signingDebugWarningLabel->setVisible(true); + } else { + m_ui->signingDebugWarningIcon->setVisible(false); + m_ui->signingDebugWarningLabel->setVisible(false); + } +} diff --git a/src/plugins/android/androiddeployqtwidget.h b/src/plugins/android/androiddeployqtwidget.h new file mode 100644 index 0000000000..e8b652ffb5 --- /dev/null +++ b/src/plugins/android/androiddeployqtwidget.h @@ -0,0 +1,86 @@ +/************************************************************************** +** +** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com> +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ +#ifndef ANDROIDDEPLOYQTWIDGET_H +#define ANDROIDDEPLOYQTWIDGET_H + +#include <projectexplorer/buildstep.h> + +#include <QWidget> + +QT_BEGIN_NAMESPACE +namespace Ui { class AndroidDeployQtWidget; } +QT_END_NAMESPACE + +namespace Qt4ProjectManager { class Qt4BuildConfiguration; } + +namespace Android { +namespace Internal { +class AndroidDeployQtStep; +class AndroidDeployQtWidget : public ProjectExplorer::BuildStepConfigWidget +{ + Q_OBJECT + +public: + AndroidDeployQtWidget(AndroidDeployQtStep *step); + ~AndroidDeployQtWidget(); + +private slots: + void setTargetSdk(const QString &sdk); + void setMinistro(); + void setDeployLocalQtLibs(); + void setBundleQtLibs(); + void installMinistro(); + void cleanLibsOnDevice(); + void resetDefaultDevices(); + void createKeyStore(); + void certificatesAliasComboBoxCurrentIndexChanged(const QString &alias); + void certificatesAliasComboBoxActivated(const QString &alias); + void activeBuildConfigurationChanged(); + void updateSigningWarning(); + void openPackageLocationCheckBoxToggled(bool checked); + void verboseOutputCheckBoxToggled(bool checked); + void browseKeyStore(); + void signPackageCheckBoxToggled(bool checked); + void updateInputFileUi(); + void inputFileComboBoxIndexChanged(); +private: + virtual QString summaryText() const; + virtual QString displayName() const; + void setCertificates(); + + Ui::AndroidDeployQtWidget *m_ui; + AndroidDeployQtStep *m_step; + Qt4ProjectManager::Qt4BuildConfiguration *m_currentBuildConfiguration; + bool m_ignoreChange; +}; + +} +} +#endif // ANDROIDDEPLOYQTWIDGET_H diff --git a/src/plugins/android/androiddeployqtwidget.ui b/src/plugins/android/androiddeployqtwidget.ui new file mode 100644 index 0000000000..d1f1461909 --- /dev/null +++ b/src/plugins/android/androiddeployqtwidget.ui @@ -0,0 +1,295 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>AndroidDeployQtWidget</class> + <widget class="QWidget" name="AndroidDeployQtWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>682</width> + <height>415</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QGridLayout" name="gridLayout_2"> + <item row="2" column="0" colspan="2"> + <widget class="QGroupBox" name="signPackage"> + <property name="title"> + <string>Sign package</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="KeystoreLocationLabel"> + <property name="text"> + <string>Keystore:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="KeystoreLocationLineEdit"> + <property name="readOnly"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="KeystoreCreatePushButton"> + <property name="text"> + <string>Create</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="KeystoreLocationPushButton"> + <property name="text"> + <string>Browse</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_4"> + <item> + <widget class="QCheckBox" name="signPackageCheckBox"> + <property name="text"> + <string>Sign package</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_3"> + <item> + <widget class="QLabel" name="signingDebugWarningIcon"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="pixmap"> + <pixmap resource="../projectexplorer/projectexplorer.qrc">:/projectexplorer/images/compile_warning.png</pixmap> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="signingDebugWarningLabel"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Signing a debug package</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="KeystoreLocationLabel_2"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Preferred" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Certificate alias:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QComboBox" name="certificatesAliasComboBox"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>300</width> + <height>0</height> + </size> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + </item> + <item row="3" column="0"> + <widget class="QGroupBox" name="qtDeployment"> + <property name="title"> + <string>Qt Deployment</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QRadioButton" name="ministroOption"> + <property name="toolTip"> + <string>Use the external Ministro application to download and maintain Qt libraries.</string> + </property> + <property name="text"> + <string>Use Ministro service to install Qt</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="temporaryQtOption"> + <property name="toolTip"> + <string>Push local Qt libraries to device. You must have Qt libraries compiled for that platform. +The APK will not be usable on any other device.</string> + </property> + <property name="text"> + <string>Deploy local Qt libraries to temporary directory</string> + </property> + </widget> + </item> + <item> + <widget class="QRadioButton" name="bundleQtOption"> + <property name="toolTip"> + <string>Creates a standalone APK.</string> + </property> + <property name="text"> + <string>Bundle Qt libraries in APK</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="3" column="1"> + <widget class="QGroupBox" name="advancedActions"> + <property name="title"> + <string>Advanced Actions</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="3" column="0"> + <widget class="QPushButton" name="cleanLibsPushButton"> + <property name="text"> + <string>Clean Temporary Libraries Directory on Device</string> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QPushButton" name="installMinistroButton"> + <property name="text"> + <string>Install Ministro from APK</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QPushButton" name="resetDefaultDevices"> + <property name="text"> + <string>Reset Default Devices</string> + </property> + </widget> + </item> + <item row="0" column="0"> + <widget class="QCheckBox" name="openPackageLocationCheckBox"> + <property name="text"> + <string>Open package location after is complete</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QCheckBox" name="verboseOutputCheckBox"> + <property name="text"> + <string>Verbose Output</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="1" column="0" colspan="2"> + <widget class="QGroupBox" name="application"> + <property name="title"> + <string>Application</string> + </property> + <layout class="QGridLayout" name="gridLayout_3"> + <item row="1" column="1"> + <widget class="QComboBox" name="targetSDKComboBox"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="targetSDKLabel"> + <property name="text"> + <string>Android target SDK:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="inputFileComboBox"/> + </item> + <item row="0" column="0"> + <widget class="QLabel" name="inputFileLabel"> + <property name="text"> + <string>Android deploy Qt Input File:</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item row="0" column="0" colspan="2"> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="oldFilesWarningIcon"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Preferred"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string/> + </property> + <property name="pixmap"> + <pixmap resource="../projectexplorer/projectexplorer.qrc">:/projectexplorer/images/compile_warning.png</pixmap> + </property> + <property name="alignment"> + <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="oldFilesWarningLabel"> + <property name="text"> + <string>Qt no longer uses the android folder in the project's source directory.</string> + </property> + <property name="wordWrap"> + <bool>true</bool> + </property> + </widget> + </item> + </layout> + </item> + </layout> + </widget> + <resources> + <include location="../projectexplorer/projectexplorer.qrc"/> + </resources> + <connections/> +</ui> diff --git a/src/plugins/android/androiddeploystep.cpp b/src/plugins/android/androiddeploystep.cpp index e004e12032..2af881aff6 100644 --- a/src/plugins/android/androiddeploystep.cpp +++ b/src/plugins/android/androiddeploystep.cpp @@ -193,44 +193,6 @@ QVariantMap AndroidDeployStep::toMap() const return map; } -void AndroidDeployStep::cleanLibsOnDevice() -{ - const QString targetArch = AndroidManager::targetArch(target()); - int deviceAPILevel = AndroidManager::minimumSDK(target()); - - AndroidDeviceInfo info = AndroidConfigurations::instance().showDeviceDialog(project(), m_deviceAPILevel, m_targetArch); - if (info.serialNumber.isEmpty()) // aborted - return; - - deviceAPILevel = info.sdk; - QString deviceSerialNumber = info.serialNumber; - - if (info.type == AndroidDeviceInfo::Emulator) { - deviceSerialNumber = AndroidConfigurations::instance().startAVD(deviceSerialNumber, deviceAPILevel, targetArch); - if (deviceSerialNumber.isEmpty()) - MessageManager::write(tr("Starting android virtual device failed.")); - } - - QProcess *process = new QProcess(this); - QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber); - arguments << QLatin1String("shell") << QLatin1String("rm") << QLatin1String("-r") << QLatin1String("/data/local/tmp/qt"); - connect(process, SIGNAL(finished(int)), this, SLOT(processFinished())); - const QString adb = AndroidConfigurations::instance().adbToolPath().toString(); - MessageManager::write(adb + QLatin1Char(' ') + arguments.join(QLatin1String(" "))); - process->start(adb, arguments); - if (!process->waitForStarted(500)) - delete process; -} - -void AndroidDeployStep::processFinished() -{ - QProcess *process = qobject_cast<QProcess *>(sender()); - QTC_ASSERT(process, return); - MessageManager::write(QString::fromLocal8Bit(process->readAll())); - MessageManager::write(tr("adb finished with exit code %1.").arg(process->exitCode())); - process->deleteLater(); -} - void AndroidDeployStep::kitUpdated(Kit *kit) { if (kit != target()->kit()) @@ -249,35 +211,6 @@ void AndroidDeployStep::kitUpdated(Kit *kit) } } -void AndroidDeployStep::installQASIPackage(const QString &packagePath) -{ - const QString targetArch = AndroidManager::targetArch(target()); - int deviceAPILevel = AndroidManager::minimumSDK(target()); - - AndroidDeviceInfo info = AndroidConfigurations::instance().showDeviceDialog(project(), m_deviceAPILevel, m_targetArch); - if (info.serialNumber.isEmpty()) // aborted - return; - - deviceAPILevel = info.sdk; - QString deviceSerialNumber = info.serialNumber; - if (info.type == AndroidDeviceInfo::Emulator) { - deviceSerialNumber = AndroidConfigurations::instance().startAVD(deviceSerialNumber, deviceAPILevel, targetArch); - if (deviceSerialNumber.isEmpty()) - MessageManager::write(tr("Starting android virtual device failed.")); - } - - QProcess *process = new QProcess(this); - QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber); - arguments << QLatin1String("install") << QLatin1String("-r ") << packagePath; - - connect(process, SIGNAL(finished(int)), this, SLOT(processFinished())); - const QString adb = AndroidConfigurations::instance().adbToolPath().toString(); - MessageManager::write(adb + QLatin1Char(' ') + arguments.join(QLatin1String(" "))); - process->start(adb, arguments); - if (!process->waitForFinished(500)) - delete process; -} - bool AndroidDeployStep::bundleQtOptionAvailable() { return m_bundleQtAvailable; @@ -338,11 +271,6 @@ QString AndroidDeployStep::deviceSerialNumber() return m_deviceSerialNumber; } -int AndroidDeployStep::deviceAPILevel() -{ - return m_deviceAPILevel; -} - unsigned int AndroidDeployStep::remoteModificationTime(const QString &fullDestination, QHash<QString, unsigned int> *cache) { QString destination = QFileInfo(fullDestination).absolutePath(); diff --git a/src/plugins/android/androiddeploystep.h b/src/plugins/android/androiddeploystep.h index 31dce0cbd6..df7ebd7e2b 100644 --- a/src/plugins/android/androiddeploystep.h +++ b/src/plugins/android/androiddeploystep.h @@ -87,15 +87,12 @@ public: virtual ~AndroidDeployStep(); QString deviceSerialNumber(); - int deviceAPILevel(); AndroidDeployAction deployAction(); bool fromMap(const QVariantMap &map); QVariantMap toMap() const; - void cleanLibsOnDevice(); - void installQASIPackage(const QString &packagePath); bool bundleQtOptionAvailable(); public slots: @@ -110,7 +107,6 @@ private slots: bool deployPackage(); void handleBuildOutput(); void handleBuildError(); - void processFinished(); void kitUpdated(ProjectExplorer::Kit *kit); private: @@ -134,6 +130,7 @@ private: void fetchRemoteModificationTimes(QList<DeployItem> *deployList); void stripFiles(const QList<DeployItem> &deployList, ProjectExplorer::Abi::Architecture architecture, const QString &ndkToolchainVersion); void deployFiles(QProcess *process, const QList<DeployItem> &deployList); + bool checkForQt51Files(); private: QString m_deviceSerialNumber; diff --git a/src/plugins/android/androiddeploystepwidget.cpp b/src/plugins/android/androiddeploystepwidget.cpp index b661bae5cf..68cb6b0c83 100644 --- a/src/plugins/android/androiddeploystepwidget.cpp +++ b/src/plugins/android/androiddeploystepwidget.cpp @@ -31,6 +31,7 @@ #include "ui_androiddeploystepwidget.h" #include "androiddeploystep.h" +#include "androidmanager.h" #include "androidrunconfiguration.h" #include <coreplugin/icore.h> @@ -116,14 +117,15 @@ void AndroidDeployStepWidget::setQASIPackagePath() QString packagePath = QFileDialog::getOpenFileName(this, tr("Qt Android Smart Installer"), QDir::homePath(), tr("Android package (*.apk)")); - if (!packagePath.isEmpty()) - m_step->installQASIPackage(packagePath); + if (!packagePath.isEmpty()) { + AndroidManager::installQASIPackage(m_step->target(), packagePath); + } } void AndroidDeployStepWidget::cleanLibsOnDevice() { - m_step->cleanLibsOnDevice(); + AndroidManager::cleanLibsOnDevice(m_step->target()); } void AndroidDeployStepWidget::resetDefaultDevices() diff --git a/src/plugins/android/androiddeploystepwidget.ui b/src/plugins/android/androiddeploystepwidget.ui index 2c44ffc55d..9db955796d 100644 --- a/src/plugins/android/androiddeploystepwidget.ui +++ b/src/plugins/android/androiddeploystepwidget.ui @@ -7,7 +7,7 @@ <x>0</x> <y>0</y> <width>682</width> - <height>155</height> + <height>183</height> </rect> </property> <property name="sizePolicy"> diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 8ed38e3e92..e87e972e84 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -35,8 +35,10 @@ #include "androidglobal.h" #include "androidpackagecreationstep.h" #include "androidtoolchain.h" +#include "androiddeployqtstep.h" #include <coreplugin/documentmanager.h> +#include <coreplugin/messagemanager.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/session.h> #include <projectexplorer/target.h> @@ -216,14 +218,23 @@ int AndroidManager::minimumSDK(ProjectExplorer::Target *target) QString AndroidManager::buildTargetSDK(ProjectExplorer::Target *target) { + QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target->kit()); + if (qt && qt->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0)) { + if (!target->activeDeployConfiguration()) + return QLatin1String("android-9"); + AndroidDeployQtStep *step = AndroidGlobal::buildStep<AndroidDeployQtStep>(target->activeDeployConfiguration()); + if (step) + return step->buildTargetSdk(); + return QLatin1String("android-9"); + } + QVariant v = target->namedSettings(QLatin1String("AndroidManager.TargetSdk")); if (v.isValid()) return v.toString(); QString fallback = QLatin1String("android-8"); - if (QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target->kit())) - if (qt->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0)) - fallback = QLatin1String("android-9"); + if (qt && qt->qtVersion() >= QtSupport::QtVersionNumber(5, 0, 0)) + fallback = QLatin1String("android-9"); if (!createAndroidTemplatesIfNecessary(target)) return AndroidConfigurations::instance().bestMatch(fallback); @@ -259,6 +270,9 @@ QString AndroidManager::targetArch(ProjectExplorer::Target *target) Utils::FileName AndroidManager::dirPath(ProjectExplorer::Target *target) { + QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(target->kit()); + if (qtVersion && qtVersion->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0)) + return target->activeBuildConfiguration()->buildDirectory().appendPath(AndroidDirName); return Utils::FileName::fromString(target->project()->projectDirectory()).appendPath(AndroidDirName); } @@ -289,15 +303,28 @@ Utils::FileName AndroidManager::srcPath(ProjectExplorer::Target *target) Utils::FileName AndroidManager::apkPath(ProjectExplorer::Target *target, BuildType buildType) { + QString packageName = QLatin1String("QtApp"); + QString buildTypeName; + if (buildType == DebugBuild) + buildTypeName = QLatin1String("debug"); + else if (buildType == ReleaseBuildUnsigned) + buildTypeName =QLatin1String("release-unsigned"); + else + buildTypeName = QLatin1String("release"); + + QtSupport::BaseQtVersion *qt = QtSupport::QtKitInformation::qtVersion(target->kit()); + if (!qt || qt->qtVersion() < QtSupport::QtVersionNumber(5, 2, 0)) { + // Qt 5.1 and earlier: + packageName = applicationName(target); + if (buildType == ReleaseBuildSigned) + buildTypeName = QLatin1String("signed"); + } + return dirPath(target) .appendPath(QLatin1String("bin")) .appendPath(QString::fromLatin1("%1-%2.apk") - .arg(applicationName(target)) - .arg(buildType == DebugBuild - ? QLatin1String("debug") - : (buildType == ReleaseBuildUnsigned) - ? QLatin1String("release-unsigned") - : QLatin1String("signed"))); + .arg(packageName) + .arg(buildTypeName)); } QStringList AndroidManager::availableTargetApplications(ProjectExplorer::Target *target) @@ -333,18 +360,59 @@ QString AndroidManager::targetApplication(ProjectExplorer::Target *target) return QString(); } +// Note, this could be implemented via a base class and a couple of virtuals +// but I intend to remove the indirection once we drop support for qt 4.8 +// and qt 5.1. 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::BundleLibraries; + AndroidDeployStep *androidDeployStep + = AndroidGlobal::buildStep<AndroidDeployStep>(target->activeDeployConfiguration()); + if (androidDeployStep) { + return androidDeployStep->deployAction() == AndroidDeployStep::BundleLibraries; + } + + AndroidDeployQtStep *androidDeployQtStep + = AndroidGlobal::buildStep<AndroidDeployQtStep>(target->activeDeployConfiguration()); + if (androidDeployQtStep) { + return androidDeployQtStep->deployAction() == AndroidDeployQtStep::BundleLibrariesDeployment; } return false; } +bool AndroidManager::useLocalLibs(ProjectExplorer::Target *target) +{ + AndroidDeployStep *androidDeployStep + = AndroidGlobal::buildStep<AndroidDeployStep>(target->activeDeployConfiguration()); + if (androidDeployStep) { + return androidDeployStep->deployAction() == AndroidDeployStep::DeployLocal + || androidDeployStep->deployAction() == AndroidDeployStep::BundleLibraries; + } + + AndroidDeployQtStep *androidDeployQtStep + = AndroidGlobal::buildStep<AndroidDeployQtStep>(target->activeDeployConfiguration()); + if (androidDeployQtStep) { + return androidDeployQtStep->deployAction() == AndroidDeployQtStep::DebugDeployment + || androidDeployQtStep->deployAction() == AndroidDeployQtStep::BundleLibrariesDeployment; + } + + return false; +} + +QString AndroidManager::deviceSerialNumber(ProjectExplorer::Target *target) +{ + AndroidDeployStep *androidDeployStep + = AndroidGlobal::buildStep<AndroidDeployStep>(target->activeDeployConfiguration()); + if (androidDeployStep) + return androidDeployStep->deviceSerialNumber(); + + AndroidDeployQtStep *androidDeployQtStep + = AndroidGlobal::buildStep<AndroidDeployQtStep>(target->activeDeployConfiguration()); + if (androidDeployQtStep) + return androidDeployQtStep->deviceSerialNumber(); + return QString(); +} + bool AndroidManager::updateDeploymentSettings(ProjectExplorer::Target *target) { // For Qt 4, the "use local libs" options is handled by passing command line arguments to the @@ -353,16 +421,16 @@ bool AndroidManager::updateDeploymentSettings(ProjectExplorer::Target *target) if (baseQtVersion == 0 || baseQtVersion->qtVersion() < QtSupport::QtVersionNumber(5,0,0)) return true; + if (baseQtVersion->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0)) + return true; + ProjectExplorer::RunConfiguration *runConfiguration = target->activeRunConfiguration(); AndroidRunConfiguration *androidRunConfiguration = qobject_cast<AndroidRunConfiguration *>(runConfiguration); if (androidRunConfiguration == 0) return false; - AndroidDeployStep *deployStep = androidRunConfiguration->deployStep(); - AndroidDeployStep::AndroidDeployAction deployAction = deployStep->deployAction(); - bool useLocalLibs = deployAction == AndroidDeployStep::DeployLocal - || deployAction == AndroidDeployStep::BundleLibraries; - bool bundleQtLibs = deployAction == AndroidDeployStep::BundleLibraries; + bool useLocalLibs = AndroidManager::useLocalLibs(target); + bool bundleQtLibs = AndroidManager::bundleQt(target); QDomDocument doc; if (!openManifest(target, doc)) @@ -465,6 +533,10 @@ bool AndroidManager::createAndroidTemplatesIfNecessary(ProjectExplorer::Target * if (!qt4Project || !qt4Project->rootProjectNode() || !version) return false; + // TODO we should create the AndroidManifest.xml file for that version + if (version->qtVersion() >= QtSupport::QtVersionNumber(5, 2, 0)) + return true; + Utils::FileName javaSrcPath = Utils::FileName::fromString(version->qmakeProperty("QT_INSTALL_PREFIX")) .appendPath(QLatin1String("src/android/java")); @@ -568,6 +640,10 @@ bool AndroidManager::createAndroidTemplatesIfNecessary(ProjectExplorer::Target * void AndroidManager::updateTarget(ProjectExplorer::Target *target, const QString &targetSDK, const QString &name) { + QtSupport::BaseQtVersion *qtVersion = QtSupport::QtKitInformation::qtVersion(target->kit()); + if (qtVersion && qtVersion->qtVersion() >= QtSupport::QtVersionNumber(5,2,0)) + return; + QString androidDir = dirPath(target).toString(); Utils::Environment env = Utils::Environment::systemEnvironment(); @@ -1115,5 +1191,126 @@ QString AndroidManager::libraryPrefix() return AndroidLibraryPrefix; } + +void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target) +{ + const QString targetArch = AndroidManager::targetArch(target); + int deviceAPILevel = AndroidManager::minimumSDK(target); + AndroidDeviceInfo info = AndroidConfigurations::instance().showDeviceDialog(target->project(), deviceAPILevel, targetArch); + if (info.serialNumber.isEmpty()) // aborted + return; + + deviceAPILevel = info.sdk; + QString deviceSerialNumber = info.serialNumber; + + if (info.type == AndroidDeviceInfo::Emulator) { + deviceSerialNumber = AndroidConfigurations::instance().startAVD(deviceSerialNumber, deviceAPILevel, targetArch); + if (deviceSerialNumber.isEmpty()) + Core::MessageManager::write(tr("Starting android virtual device failed.")); + } + + QProcess *process = new QProcess(); + QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber); + arguments << QLatin1String("shell") << QLatin1String("rm") << QLatin1String("-r") << QLatin1String("/data/local/tmp/qt"); + process->connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); + const QString adb = AndroidConfigurations::instance().adbToolPath().toString(); + Core::MessageManager::write(adb + QLatin1Char(' ') + arguments.join(QLatin1String(" "))); + process->start(adb, arguments); + if (!process->waitForStarted(500)) + delete process; +} + +void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const QString &packagePath) +{ + const QString targetArch = AndroidManager::targetArch(target); + int deviceAPILevel = AndroidManager::minimumSDK(target); + AndroidDeviceInfo info = AndroidConfigurations::instance().showDeviceDialog(target->project(), deviceAPILevel, targetArch); + if (info.serialNumber.isEmpty()) // aborted + return; + + deviceAPILevel = info.sdk; + QString deviceSerialNumber = info.serialNumber; + if (info.type == AndroidDeviceInfo::Emulator) { + deviceSerialNumber = AndroidConfigurations::instance().startAVD(deviceSerialNumber, deviceAPILevel, targetArch); + if (deviceSerialNumber.isEmpty()) + Core::MessageManager::write(tr("Starting android virtual device failed.")); + } + + QProcess *process = new QProcess(); + QStringList arguments = AndroidDeviceInfo::adbSelector(deviceSerialNumber); + arguments << QLatin1String("install") << QLatin1String("-r ") << packagePath; + + process->connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); + const QString adb = AndroidConfigurations::instance().adbToolPath().toString(); + Core::MessageManager::write(adb + QLatin1Char(' ') + arguments.join(QLatin1String(" "))); + process->start(adb, arguments); + if (!process->waitForFinished(500)) + delete process; + +} + +bool AndroidManager::checkKeystorePassword(const QString &keystorePath, const QString &keystorePasswd) +{ + if (keystorePasswd.isEmpty()) + return false; + QStringList arguments; + arguments << QLatin1String("-list") + << QLatin1String("-keystore") + << keystorePath + << QLatin1String("--storepass") + << keystorePasswd; + QProcess proc; + proc.start(AndroidConfigurations::instance().keytoolPath().toString(), arguments); + if (!proc.waitForStarted(500)) + return false; + if (!proc.waitForFinished(500)) { + proc.kill(); + proc.waitForFinished(); + return false; + } + return proc.exitCode() == 0; +} + +bool AndroidManager::checkCertificatePassword(const QString &keystorePath, const QString &keystorePasswd, const QString &alias, const QString &certificatePasswd) +{ + // assumes that the keystore password is correct + QStringList arguments; + arguments << QLatin1String("-certreq") + << QLatin1String("-keystore") + << keystorePath + << QLatin1String("--storepass") + << keystorePasswd + << QLatin1String("-alias") + << alias + << QLatin1String("-keypass"); + if (certificatePasswd.isEmpty()) + arguments << keystorePasswd; + else + arguments << certificatePasswd; + + QProcess proc; + proc.start(AndroidConfigurations::instance().keytoolPath().toString(), arguments); + if (!proc.waitForStarted(500)) + return false; + if (!proc.waitForFinished(500)) { + proc.kill(); + proc.waitForFinished(); + return false; + } + return proc.exitCode() == 0; +} + +bool AndroidManager::checkForQt51Files(const QString &projectDirectory) +{ + Utils::FileName fileName = Utils::FileName::fromString(projectDirectory); + fileName.appendPath(QLatin1String("android")).appendPath(QLatin1String("version.xml")); + if (!fileName.toFileInfo().exists()) + return false; + QDomDocument dstVersionDoc; + if (!AndroidManager::openXmlFile(dstVersionDoc, fileName)) + return false; + return dstVersionDoc.documentElement().attribute(QLatin1String("value")).toDouble() < 5.2; +} + } // namespace Internal } // namespace Qt4ProjectManager diff --git a/src/plugins/android/androidmanager.h b/src/plugins/android/androidmanager.h index f6ac5f038b..df81322e53 100644 --- a/src/plugins/android/androidmanager.h +++ b/src/plugins/android/androidmanager.h @@ -72,6 +72,8 @@ public: static bool updateDeploymentSettings(ProjectExplorer::Target *target); static bool bundleQt(ProjectExplorer::Target *target); + static bool useLocalLibs(ProjectExplorer::Target *target); + static QString deviceSerialNumber(ProjectExplorer::Target *target); static QString buildTargetSDK(ProjectExplorer::Target *target); static bool setBuildTargetSDK(ProjectExplorer::Target *target, const QString &sdk); @@ -128,6 +130,12 @@ public: static QString libGnuStl(const QString &arch, const QString &ndkToolChainVersion); static QString libraryPrefix(); + static void cleanLibsOnDevice(ProjectExplorer::Target *target); + static void installQASIPackage(ProjectExplorer::Target *target, const QString &packagePath); + + static bool checkKeystorePassword(const QString &keystorePath, const QString &keystorePasswd); + static bool checkCertificatePassword(const QString &keystorePath, const QString &keystorePasswd, const QString &alias, const QString &certificatePasswd); + static bool checkForQt51Files(const QString &projectDirectory); private: static void raiseError(const QString &reason); static bool openXmlFile(QDomDocument &doc, const Utils::FileName &fileName); diff --git a/src/plugins/android/androidpackagecreationstep.cpp b/src/plugins/android/androidpackagecreationstep.cpp index e337acd361..26d4f70306 100644 --- a/src/plugins/android/androidpackagecreationstep.cpp +++ b/src/plugins/android/androidpackagecreationstep.cpp @@ -36,6 +36,7 @@ #include "androidmanager.h" #include "androidgdbserverkitinformation.h" #include "androidtoolchain.h" +#include "certificatesmodel.h" #include <projectexplorer/buildsteplist.h> #include <projectexplorer/projectexplorerconstants.h> @@ -73,47 +74,6 @@ namespace { using namespace Qt4ProjectManager; -class CertificatesModel: public QAbstractListModel -{ -public: - CertificatesModel(const QString &rowCertificates, QObject *parent) - : QAbstractListModel(parent) - { - int from = rowCertificates.indexOf(AliasString); - QPair<QString, QString> item; - while (from > -1) { - from += 11;// strlen(AliasString); - const int eol = rowCertificates.indexOf(QLatin1Char('\n'), from); - item.first = rowCertificates.mid(from, eol - from).trimmed(); - const int eoc = rowCertificates.indexOf(CertificateSeparator, eol); - item.second = rowCertificates.mid(eol + 1, eoc - eol - 2).trimmed(); - from = rowCertificates.indexOf(AliasString, eoc); - m_certs.push_back(item); - } - } - -protected: - int rowCount(const QModelIndex &parent = QModelIndex()) const - { - if (parent.isValid()) - return 0; - return m_certs.size(); - } - - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const - { - if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) - return QVariant(); - if (role == Qt::DisplayRole) - return m_certs[index.row()].first; - return m_certs[index.row()].second; - } - -private: - QVector<QPair<QString, QString> > m_certs; -}; - - AndroidPackageCreationStep::AndroidPackageCreationStep(BuildStepList *bsl) : BuildStep(bsl, CreatePackageId) { diff --git a/src/plugins/android/androidpackagecreationwidget.ui b/src/plugins/android/androidpackagecreationwidget.ui index 69695797d6..5f27b1d6e9 100644 --- a/src/plugins/android/androidpackagecreationwidget.ui +++ b/src/plugins/android/androidpackagecreationwidget.ui @@ -37,7 +37,7 @@ </widget> </item> <item> - <widget class="QGroupBox" name="groupBox_2"> + <widget class="QGroupBox" name="librariesGroupBox"> <property name="title"> <string>Libraries</string> </property> diff --git a/src/plugins/android/androidpackageinstallationfactory.cpp b/src/plugins/android/androidpackageinstallationfactory.cpp index e3f86b0e59..e054f903c8 100644 --- a/src/plugins/android/androidpackageinstallationfactory.cpp +++ b/src/plugins/android/androidpackageinstallationfactory.cpp @@ -50,13 +50,8 @@ AndroidPackageInstallationFactory::AndroidPackageInstallationFactory(QObject *pa QList<Core::Id> AndroidPackageInstallationFactory::availableCreationIds(BuildStepList *parent) const { - if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) - return QList<Core::Id>(); - if (!AndroidManager::supportsAndroid(parent->target())) - return QList<Core::Id>(); - if (parent->contains(AndroidPackageInstallationStep::Id)) - return QList<Core::Id>(); - return QList<Core::Id>() << AndroidPackageInstallationStep::Id; + Q_UNUSED(parent); + return QList<Core::Id>(); } QString AndroidPackageInstallationFactory::displayNameForId(const Core::Id id) const @@ -68,25 +63,33 @@ QString AndroidPackageInstallationFactory::displayNameForId(const Core::Id id) c bool AndroidPackageInstallationFactory::canCreate(BuildStepList *parent, const Core::Id id) const { - return availableCreationIds(parent).contains(id); + Q_UNUSED(parent); + Q_UNUSED(id); + return false; } BuildStep *AndroidPackageInstallationFactory::create(BuildStepList *parent, const Core::Id id) { - Q_ASSERT(canCreate(parent, id)); + Q_UNUSED(parent); Q_UNUSED(id); - return new AndroidPackageInstallationStep(parent); + return 0; } bool AndroidPackageInstallationFactory::canRestore(BuildStepList *parent, const QVariantMap &map) const { - return canCreate(parent, idFromMap(map)); + if (parent->id() != ProjectExplorer::Constants::BUILDSTEPS_DEPLOY) + return false; + if (!AndroidManager::supportsAndroid(parent->target())) + return false; + if (parent->contains(AndroidPackageInstallationStep::Id)) + return false; + return ProjectExplorer::idFromMap(map) == AndroidPackageInstallationStep::Id; } BuildStep *AndroidPackageInstallationFactory::restore(BuildStepList *parent, const QVariantMap &map) { Q_ASSERT(canRestore(parent, map)); - AndroidPackageInstallationStep * const step = new AndroidPackageInstallationStep(parent); + AndroidPackageInstallationStep * const step = new AndroidPackageInstallationStep(AndroidPackageInstallationStep::ProjectDirectory, parent); if (!step->fromMap(map)) { delete step; return 0; diff --git a/src/plugins/android/androidpackageinstallationstep.cpp b/src/plugins/android/androidpackageinstallationstep.cpp index 7b69ac3467..8541c3ef02 100644 --- a/src/plugins/android/androidpackageinstallationstep.cpp +++ b/src/plugins/android/androidpackageinstallationstep.cpp @@ -40,9 +40,12 @@ using namespace Android::Internal; const Core::Id AndroidPackageInstallationStep::Id = Core::Id("Qt4ProjectManager.AndroidPackageInstallationStep"); +namespace { +const char ANDROIDDIRECTORY[] = "Android.AndroidPackageInstallationStep.AndroidDirectory"; +} -AndroidPackageInstallationStep::AndroidPackageInstallationStep(ProjectExplorer::BuildStepList *bsl) - : MakeStep(bsl, Id) +AndroidPackageInstallationStep::AndroidPackageInstallationStep(AndroidDirectory mode,ProjectExplorer::BuildStepList *bsl) + : MakeStep(bsl, Id), m_androidDirectory(mode) { const QString name = tr("Copy application data"); setDefaultDisplayName(name); @@ -58,7 +61,11 @@ bool AndroidPackageInstallationStep::init() ProjectExplorer::BuildConfiguration *bc = buildConfiguration(); if (!bc) bc = target()->activeBuildConfiguration(); - QString dirPath = AndroidManager::dirPath(target()).toString(); + QString dirPath; + if (m_androidDirectory == ProjectDirectory) + dirPath = AndroidManager::dirPath(target()).toString(); + else + dirPath = bc->buildDirectory().appendPath((QLatin1String("android"))).toString(); if (Utils::HostOsInfo::isWindowsHost()) if (bc->environment().searchInPath(QLatin1String("sh.exe")).isEmpty()) dirPath = QDir::toNativeSeparators(dirPath); @@ -66,3 +73,18 @@ bool AndroidPackageInstallationStep::init() return MakeStep::init(); } + +bool AndroidPackageInstallationStep::fromMap(const QVariantMap &map) +{ + if (!MakeStep::fromMap(map)) + return false; + m_androidDirectory = AndroidDirectory(map.value(QLatin1String(ANDROIDDIRECTORY)).toInt()); + return true; +} + +QVariantMap AndroidPackageInstallationStep::toMap() const +{ + QVariantMap map = MakeStep::toMap(); + map.insert(QLatin1String(ANDROIDDIRECTORY), m_androidDirectory); + return map; +} diff --git a/src/plugins/android/androidpackageinstallationstep.h b/src/plugins/android/androidpackageinstallationstep.h index 6763ef138e..2ccbc7ad69 100644 --- a/src/plugins/android/androidpackageinstallationstep.h +++ b/src/plugins/android/androidpackageinstallationstep.h @@ -41,12 +41,17 @@ class AndroidPackageInstallationStep : public Qt4ProjectManager::MakeStep friend class AndroidPackageInstallationFactory; public: - explicit AndroidPackageInstallationStep(ProjectExplorer::BuildStepList *bsl); + enum AndroidDirectory { ProjectDirectory, BuildDirectory }; + explicit AndroidPackageInstallationStep(AndroidDirectory mode, ProjectExplorer::BuildStepList *bsl); bool init(); + bool fromMap(const QVariantMap &map); + QVariantMap toMap() const; + private: AndroidPackageInstallationStep(ProjectExplorer::BuildStepList *bc, AndroidPackageInstallationStep *other); + AndroidDirectory m_androidDirectory; static const Core::Id Id; }; diff --git a/src/plugins/android/androidplugin.cpp b/src/plugins/android/androidplugin.cpp index 508b4ce93d..41ebb56134 100644 --- a/src/plugins/android/androidplugin.cpp +++ b/src/plugins/android/androidplugin.cpp @@ -32,6 +32,7 @@ #include "androidconstants.h" #include "androidconfigurations.h" #include "androiddeploystepfactory.h" +#include "androiddeployqtstep.h" #include "androiddevice.h" #include "androiddevicefactory.h" #include "androidmanager.h" @@ -74,6 +75,7 @@ bool AndroidPlugin::initialize(const QStringList &arguments, QString *errorMessa addAutoReleasedObject(new Internal::AndroidPackageInstallationFactory); addAutoReleasedObject(new Internal::AndroidPackageCreationFactory); addAutoReleasedObject(new Internal::AndroidDeployStepFactory); + addAutoReleasedObject(new Internal::AndroidDeployQtStepFactory); addAutoReleasedObject(new Internal::AndroidSettingsPage); addAutoReleasedObject(new Internal::AndroidQtVersionFactory); addAutoReleasedObject(new Internal::AndroidToolChainFactory); diff --git a/src/plugins/android/androidrunconfiguration.cpp b/src/plugins/android/androidrunconfiguration.cpp index 45e9e091b9..d84bce54b8 100644 --- a/src/plugins/android/androidrunconfiguration.cpp +++ b/src/plugins/android/androidrunconfiguration.cpp @@ -84,16 +84,6 @@ AndroidConfig AndroidRunConfiguration::config() const return AndroidConfigurations::instance().config(); } -AndroidDeployStep *AndroidRunConfiguration::deployStep() const -{ - AndroidDeployStep * const step - = AndroidGlobal::buildStep<AndroidDeployStep>(target()->activeDeployConfiguration()); - Q_ASSERT_X(step, Q_FUNC_INFO, - "Impossible: Android build configuration without deploy step."); - return step; -} - - const QString AndroidRunConfiguration::remoteChannel() const { return QLatin1String(":5039"); diff --git a/src/plugins/android/androidrunconfiguration.h b/src/plugins/android/androidrunconfiguration.h index 947d44a385..9e0fd50cd7 100644 --- a/src/plugins/android/androidrunconfiguration.h +++ b/src/plugins/android/androidrunconfiguration.h @@ -52,8 +52,6 @@ public: QWidget *createConfigurationWidget(); Utils::OutputFormatter *createOutputFormatter() const; - AndroidDeployStep *deployStep() const; - void setArguments(const QString &args); AndroidConfig config() const; QString proFilePath() const; diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index ef467d2bca..e00cb95e68 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -30,6 +30,7 @@ #include "androidrunner.h" #include "androiddeploystep.h" +#include "androiddeployqtstep.h" #include "androidconfigurations.h" #include "androidglobal.h" #include "androidrunconfiguration.h" @@ -73,17 +74,17 @@ AndroidRunner::AndroidRunner(QObject *parent, m_qmlPort = server.serverPort(); } ProjectExplorer::Target *target = runConfig->target(); - AndroidDeployStep *ds = runConfig->deployStep(); - m_useLocalQtLibs = ds->deployAction() == AndroidDeployStep::DeployLocal - || ds->deployAction() == AndroidDeployStep::BundleLibraries; + m_useLocalQtLibs = AndroidManager::useLocalLibs(target); if (m_useLocalQtLibs) { - m_localLibs = AndroidManager::loadLocalLibs(target, ds->deviceAPILevel()); - m_localJars = AndroidManager::loadLocalJars(target, ds->deviceAPILevel()); - m_localJarsInitClasses = AndroidManager::loadLocalJarsInitClasses(target, ds->deviceAPILevel()); + int deviceApiLevel = AndroidManager::minimumSDK(target); + m_localLibs = AndroidManager::loadLocalLibs(target, deviceApiLevel); + m_localJars = AndroidManager::loadLocalJars(target, deviceApiLevel); + m_localJarsInitClasses = AndroidManager::loadLocalJarsInitClasses(target, deviceApiLevel); } m_intentName = AndroidManager::intentName(target); m_packageName = m_intentName.left(m_intentName.indexOf(QLatin1Char('/'))); - m_deviceSerialNumber = ds->deviceSerialNumber(); + + m_deviceSerialNumber = AndroidManager::deviceSerialNumber(target); m_processPID = -1; m_adb = AndroidConfigurations::instance().adbToolPath().toString(); m_selector = AndroidDeviceInfo::adbSelector(m_deviceSerialNumber); diff --git a/src/plugins/android/certificatesmodel.cpp b/src/plugins/android/certificatesmodel.cpp new file mode 100644 index 0000000000..c06d1b72d1 --- /dev/null +++ b/src/plugins/android/certificatesmodel.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com> +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#include "certificatesmodel.h" + +using namespace Android; +using namespace Android::Internal; + +namespace { +const QLatin1String AliasString("Alias name:"); +const QLatin1String CertificateSeparator("*******************************************"); +} + +CertificatesModel::CertificatesModel(const QString &rowCertificates, QObject *parent) + : QAbstractListModel(parent) +{ + int from = rowCertificates.indexOf(AliasString); + QPair<QString, QString> item; + while (from > -1) { + from += 11;// strlen(AliasString); + const int eol = rowCertificates.indexOf(QLatin1Char('\n'), from); + item.first = rowCertificates.mid(from, eol - from).trimmed(); + const int eoc = rowCertificates.indexOf(CertificateSeparator, eol); + item.second = rowCertificates.mid(eol + 1, eoc - eol - 2).trimmed(); + from = rowCertificates.indexOf(AliasString, eoc); + m_certs.push_back(item); + } +} + +int CertificatesModel::rowCount(const QModelIndex &parent) const +{ + if (parent.isValid()) + return 0; + return m_certs.size(); +} + +QVariant CertificatesModel::data(const QModelIndex &index, int role) const +{ + if (!index.isValid() || (role != Qt::DisplayRole && role != Qt::ToolTipRole)) + return QVariant(); + if (role == Qt::DisplayRole) + return m_certs[index.row()].first; + return m_certs[index.row()].second; +} diff --git a/src/plugins/android/certificatesmodel.h b/src/plugins/android/certificatesmodel.h new file mode 100644 index 0000000000..fa0d53c219 --- /dev/null +++ b/src/plugins/android/certificatesmodel.h @@ -0,0 +1,56 @@ +/**************************************************************************** +** +** Copyright (c) 2013 BogDan Vatra <bog_dan_ro@yahoo.com> +** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +****************************************************************************/ + +#ifndef CERTIFICATESMODEL_H +#define CERTIFICATESMODEL_H + +#include <QAbstractListModel> + +namespace Android { +namespace Internal { + +class CertificatesModel: public QAbstractListModel +{ +public: + CertificatesModel(const QString &rowCertificates, QObject *parent); + +protected: + int rowCount(const QModelIndex &parent = QModelIndex()) const; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + +private: + QVector<QPair<QString, QString> > m_certs; +}; + +} +} + +#endif // CERTIFICATESMODEL_H diff --git a/src/plugins/qt4projectmanager/qt4nodes.cpp b/src/plugins/qt4projectmanager/qt4nodes.cpp index cad79fcf8d..8fc2f68b56 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.cpp +++ b/src/plugins/qt4projectmanager/qt4nodes.cpp @@ -1963,6 +1963,7 @@ void Qt4ProFileNode::applyEvaluate(EvalResult evalResult, bool async) newVarValues[StaticLibExtensionVar] = m_readerExact->values(QLatin1String("QMAKE_EXTENSION_STATICLIB")); newVarValues[ShLibExtensionVar] = m_readerExact->values(QLatin1String("QMAKE_EXTENSION_SHLIB")); newVarValues[AndroidArchVar] = m_readerExact->values(QLatin1String("ANDROID_TARGET_ARCH")); + newVarValues[AndroidDeploySettingsFile] = m_readerExact->values(QLatin1String("ANDROID_DEPLOYMENT_SETTINGS_FILE")); m_isDeployable = false; if (m_projectType == ApplicationTemplate) { diff --git a/src/plugins/qt4projectmanager/qt4nodes.h b/src/plugins/qt4projectmanager/qt4nodes.h index 3e657f7e19..d4b255bcf3 100644 --- a/src/plugins/qt4projectmanager/qt4nodes.h +++ b/src/plugins/qt4projectmanager/qt4nodes.h @@ -105,7 +105,8 @@ enum Qt4Variable { TargetVersionExtVar, StaticLibExtensionVar, ShLibExtensionVar, - AndroidArchVar + AndroidArchVar, + AndroidDeploySettingsFile }; // Import base classes into namespace |