Merge remote-tracking branch 'origin/4.5'

Change-Id: Iebd340258966aff910fb9ab0e45b5149e8f242f0
This commit is contained in:
Eike Ziller
2017-10-12 09:49:14 +02:00
73 changed files with 3874 additions and 1032 deletions

View File

@@ -50,7 +50,10 @@ HEADERS += \
androidsdkmanager.h \
androidavdmanager.h \
androidrunconfigurationwidget.h \
adbcommandswidget.h
adbcommandswidget.h \
androidsdkpackage.h \
androidsdkmodel.h \
androidsdkmanagerwidget.h
SOURCES += \
androidconfigurations.cpp \
@@ -94,7 +97,10 @@ SOURCES += \
androidsdkmanager.cpp \
androidavdmanager.cpp \
androidrunconfigurationwidget.cpp \
adbcommandswidget.cpp
adbcommandswidget.cpp \
androidsdkpackage.cpp \
androidsdkmodel.cpp \
androidsdkmanagerwidget.cpp
FORMS += \
androidsettingswidget.ui \
@@ -104,7 +110,8 @@ FORMS += \
androiddeployqtwidget.ui \
androidbuildapkwidget.ui \
androidrunconfigurationwidget.ui \
adbcommandswidget.ui
adbcommandswidget.ui \
androidsdkmanagerwidget.ui
RESOURCES = android.qrc

View File

@@ -54,8 +54,6 @@ const char avdInfoAbiKey[] = "abi.type";
const char avdInfoTargetKey[] = "target";
const char avdInfoErrorKey[] = "Error:";
const QVersionNumber avdManagerIntroVersion(25, 3 ,0);
const int avdCreateTimeoutMs = 30000;
/*!
@@ -101,37 +99,37 @@ static bool checkForTimeout(const chrono::steady_clock::time_point &start,
return timedOut;
}
static AndroidConfig::CreateAvdInfo createAvdCommand(const AndroidConfig config,
const AndroidConfig::CreateAvdInfo &info)
static CreateAvdInfo createAvdCommand(const AndroidConfig config, const CreateAvdInfo &info)
{
AndroidConfig::CreateAvdInfo result = info;
CreateAvdInfo result = info;
if (!result.isValid()) {
qCDebug(avdManagerLog) << "AVD Create failed. Invalid CreateAvdInfo" << result.name
<< result.target.name << result.target.apiLevel;
<< result.sdkPlatform->displayText() << result.sdkPlatform->apiLevel();
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Invalid input.");
return result;
}
QStringList arguments({"create", "avd", "-k", result.target.package, "-n", result.name});
QStringList arguments({"create", "avd", "-k", result.sdkPlatform->sdkStylePath(), "-n", result.name});
if (!result.abi.isEmpty()) {
SystemImage image = Utils::findOrDefault(result.target.systemImages,
SystemImage *image = Utils::findOrDefault(result.sdkPlatform->systemImages(),
Utils::equal(&SystemImage::abiName, result.abi));
if (image.isValid()) {
arguments << "-k" << image.package;
if (image && image->isValid()) {
arguments << "-k" << image->sdkStylePath();
} else {
QString name = result.sdkPlatform->displayText();
qCDebug(avdManagerLog) << "AVD Create failed. Cannot find system image for the platform"
<< result.abi << result.target.name;
<< result.abi << name;
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Cannot find system image for "
"the ABI %1(%2).").arg(result.abi).arg(result.target.name);
"the ABI %1(%2).").arg(result.abi).arg(name);
return result;
}
} else {
arguments << "-k" << result.target.package;
arguments << "-k" << result.sdkPlatform->sdkStylePath();
}
if (result.sdcardSize > 0)
@@ -219,14 +217,9 @@ AndroidAvdManager::~AndroidAvdManager()
}
bool AndroidAvdManager::avdManagerUiToolAvailable() const
{
return m_config.sdkToolsVersion() < avdManagerIntroVersion;
}
void AndroidAvdManager::launchAvdManagerUiTool() const
{
if (avdManagerUiToolAvailable()) {
if (m_config.useNativeUiTools()) {
m_androidTool->launchAvdManager();
} else {
qCDebug(avdManagerLog) << "AVD Ui tool launch failed. UI tool not available"
@@ -234,10 +227,9 @@ void AndroidAvdManager::launchAvdManagerUiTool() const
}
}
QFuture<AndroidConfig::CreateAvdInfo>
AndroidAvdManager::createAvd(AndroidConfig::CreateAvdInfo info) const
QFuture<CreateAvdInfo> AndroidAvdManager::createAvd(CreateAvdInfo info) const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
if (m_config.useNativeUiTools())
return m_androidTool->createAvd(info);
return Utils::runAsync(&createAvdCommand, m_config, info);
@@ -245,7 +237,7 @@ AndroidAvdManager::createAvd(AndroidConfig::CreateAvdInfo info) const
bool AndroidAvdManager::removeAvd(const QString &name) const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
if (m_config.useNativeUiTools())
return m_androidTool->removeAvd(name);
Utils::SynchronousProcess proc;
@@ -258,7 +250,7 @@ bool AndroidAvdManager::removeAvd(const QString &name) const
QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
if (m_config.useNativeUiTools())
return m_androidTool->androidVirtualDevicesFuture();
return Utils::runAsync(&AvdManagerOutputParser::listVirtualDevices, m_parser.get(), m_config);

View File

@@ -40,9 +40,8 @@ public:
AndroidAvdManager(const AndroidConfig& config = AndroidConfigurations::currentConfig());
~AndroidAvdManager();
bool avdManagerUiToolAvailable() const;
void launchAvdManagerUiTool() const;
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
QFuture<CreateAvdInfo> createAvd(CreateAvdInfo info) const;
bool removeAvd(const QString &name) const;
QFuture<AndroidDeviceInfoList> avdList() const;

View File

@@ -29,6 +29,7 @@
#include "androidconfigurations.h"
#include "androidconstants.h"
#include "androidmanager.h"
#include "androidsdkmanager.h"
#include "androidqtsupport.h"
#include "certificatesmodel.h"
@@ -92,7 +93,8 @@ private:
AndroidBuildApkStep::AndroidBuildApkStep(ProjectExplorer::BuildStepList *parent, const Core::Id id)
: ProjectExplorer::AbstractProcessStep(parent, id),
m_buildTargetSdk(AndroidConfig::apiLevelNameFor(AndroidConfigurations::currentConfig().highestAndroidSdk()))
m_buildTargetSdk(AndroidConfig::apiLevelNameFor(AndroidConfigurations::
sdkManager()->latestAndroidSdkPlatform()))
{
//: AndroidBuildApkStep default display name
setDefaultDisplayName(tr("Build Android APK"));
@@ -233,8 +235,10 @@ bool AndroidBuildApkStep::fromMap(const QVariantMap &map)
m_keystorePath = Utils::FileName::fromString(map.value(KeystoreLocationKey).toString());
m_signPackage = false; // don't restore this
m_buildTargetSdk = map.value(BuildTargetSdkKey).toString();
if (m_buildTargetSdk.isEmpty())
m_buildTargetSdk = AndroidConfig::apiLevelNameFor(AndroidConfigurations::currentConfig().highestAndroidSdk());
if (m_buildTargetSdk.isEmpty()) {
m_buildTargetSdk = AndroidConfig::apiLevelNameFor(AndroidConfigurations::
sdkManager()->latestAndroidSdkPlatform());
}
m_verbose = map.value(VerboseOutputKey).toBool();
m_useMinistro = map.value(UseMinistroKey).toBool();
return ProjectExplorer::BuildStep::fromMap(map);

View File

@@ -29,6 +29,7 @@
#include "androidconfigurations.h"
#include "androidcreatekeystorecertificate.h"
#include "androidmanager.h"
#include "androidsdkmanager.h"
#include "ui_androidbuildapkwidget.h"
#include <projectexplorer/buildconfiguration.h>
@@ -47,6 +48,8 @@
using namespace Android;
using namespace Internal;
const int minApiSupported = 9;
AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
: ProjectExplorer::BuildStepConfigWidget(),
m_ui(new Ui::AndroidBuildApkWidget),
@@ -55,9 +58,8 @@ AndroidBuildApkWidget::AndroidBuildApkWidget(AndroidBuildApkStep *step)
m_ui->setupUi(this);
// Target sdk combobox
int minApiLevel = 9;
const AndroidConfig &config = AndroidConfigurations::currentConfig();
QStringList targets = AndroidConfig::apiLevelNamesFor(config.sdkTargets(minApiLevel));
QStringList targets = AndroidConfig::apiLevelNamesFor(AndroidConfigurations::sdkManager()->
filteredSdkPlatforms(minApiSupported));
targets.removeDuplicates();
m_ui->targetSDKComboBox->addItems(targets);
m_ui->targetSDKComboBox->setCurrentIndex(targets.indexOf(AndroidManager::buildTargetSDK(step->target())));

View File

@@ -87,6 +87,7 @@ namespace {
const QLatin1String SettingsGroup("AndroidConfigurations");
const QLatin1String SDKLocationKey("SDKLocation");
const QLatin1String SDKManagerToolArgsKey("SDKManagerToolArgs");
const QLatin1String NDKLocationKey("NDKLocation");
const QLatin1String OpenJDKLocationKey("OpenJDKLocation");
const QLatin1String KeystoreLocationKey("KeystoreLocation");
@@ -246,6 +247,7 @@ void AndroidConfig::load(const QSettings &settings)
// user settings
m_partitionSize = settings.value(PartitionSizeKey, 1024).toInt();
m_sdkLocation = FileName::fromString(settings.value(SDKLocationKey).toString());
m_sdkManagerToolArgs = settings.value(SDKManagerToolArgsKey).toStringList();
m_ndkLocation = FileName::fromString(settings.value(NDKLocationKey).toString());
m_openJDKLocation = FileName::fromString(settings.value(OpenJDKLocationKey).toString());
m_keystoreLocation = FileName::fromString(settings.value(KeystoreLocationKey).toString());
@@ -261,6 +263,7 @@ void AndroidConfig::load(const QSettings &settings)
&& settings.value(changeTimeStamp).toInt() != QFileInfo(sdkSettingsFileName()).lastModified().toMSecsSinceEpoch() / 1000) {
// persisten settings
m_sdkLocation = FileName::fromString(reader.restoreValue(SDKLocationKey, m_sdkLocation.toString()).toString());
m_sdkManagerToolArgs = reader.restoreValue(SDKManagerToolArgsKey, m_sdkManagerToolArgs).toStringList();
m_ndkLocation = FileName::fromString(reader.restoreValue(NDKLocationKey, m_ndkLocation.toString()).toString());
m_openJDKLocation = FileName::fromString(reader.restoreValue(OpenJDKLocationKey, m_openJDKLocation.toString()).toString());
m_keystoreLocation = FileName::fromString(reader.restoreValue(KeystoreLocationKey, m_keystoreLocation.toString()).toString());
@@ -272,7 +275,6 @@ void AndroidConfig::load(const QSettings &settings)
m_makeExtraSearchDirectories << extraDirectory;
// persistent settings
}
m_availableSdkPlatformsUpToDate = false;
m_NdkInformationUpToDate = false;
}
@@ -284,6 +286,7 @@ void AndroidConfig::save(QSettings &settings) const
// user settings
settings.setValue(SDKLocationKey, m_sdkLocation.toString());
settings.setValue(SDKManagerToolArgsKey, m_sdkManagerToolArgs);
settings.setValue(NDKLocationKey, m_ndkLocation.toString());
settings.setValue(OpenJDKLocationKey, m_openJDKLocation.toString());
settings.setValue(KeystoreLocationKey, m_keystoreLocation.toString());
@@ -333,40 +336,15 @@ void AndroidConfig::updateNdkInformation() const
m_NdkInformationUpToDate = true;
}
void AndroidConfig::updateAvailableSdkPlatforms() const
{
if (m_availableSdkPlatformsUpToDate)
return;
m_availableSdkPlatforms.clear();
AndroidSdkManager sdkManager(*this);
bool success = false;
m_availableSdkPlatforms = sdkManager.availableSdkPlatforms(&success);
if (success)
m_availableSdkPlatformsUpToDate = true;
}
QStringList AndroidConfig::apiLevelNamesFor(const QList<SdkPlatform> &platforms)
QStringList AndroidConfig::apiLevelNamesFor(const SdkPlatformList &platforms)
{
return Utils::transform(platforms, AndroidConfig::apiLevelNameFor);
}
QString AndroidConfig::apiLevelNameFor(const SdkPlatform &platform)
QString AndroidConfig::apiLevelNameFor(const SdkPlatform *platform)
{
return platform.apiLevel > 0 ? QString("android-%1").arg(platform.apiLevel) : "";
}
QList<SdkPlatform> AndroidConfig::sdkTargets(int minApiLevel) const
{
updateAvailableSdkPlatforms();
QList<SdkPlatform> result;
for (int i = 0; i < m_availableSdkPlatforms.size(); ++i) {
if (m_availableSdkPlatforms.at(i).apiLevel >= minApiLevel)
result << m_availableSdkPlatforms.at(i);
else
break;
}
return result;
return platform && platform->apiLevel() > 0 ?
QString("android-%1").arg(platform->apiLevel()) : "";
}
FileName AndroidConfig::adbToolPath() const
@@ -523,20 +501,6 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const QString &adbToo
return devices;
}
AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent, int minApiLevel, QString targetArch) const
{
CreateAvdInfo result;
AvdDialog d(minApiLevel, targetArch, this, parent);
if (d.exec() != QDialog::Accepted || !d.isValid())
return result;
result.target = d.target();
result.name = d.name();
result.abi = d.abi();
result.sdcardSize = d.sdcardSize();
return result;
}
bool AndroidConfig::isConnected(const QString &serialNumber) const
{
QVector<AndroidDeviceInfo> devices = connectedDevices();
@@ -716,12 +680,10 @@ QStringList AndroidConfig::getAbis(const QString &adbToolPath, const QString &de
return result;
}
SdkPlatform AndroidConfig::highestAndroidSdk() const
bool AndroidConfig::useNativeUiTools() const
{
updateAvailableSdkPlatforms();
if (m_availableSdkPlatforms.isEmpty())
return SdkPlatform();
return m_availableSdkPlatforms.first();
const QVersionNumber version = sdkToolsVersion();
return !version.isNull() && version <= QVersionNumber(25, 3 ,0);
}
QString AndroidConfig::bestNdkPlatformMatch(int target) const
@@ -743,7 +705,6 @@ FileName AndroidConfig::sdkLocation() const
void AndroidConfig::setSdkLocation(const FileName &sdkLocation)
{
m_sdkLocation = sdkLocation;
m_availableSdkPlatformsUpToDate = false;
}
QVersionNumber AndroidConfig::sdkToolsVersion() const
@@ -770,6 +731,17 @@ QVersionNumber AndroidConfig::buildToolsVersion() const
return maxVersion;
}
QStringList AndroidConfig::sdkManagerToolArgs() const
{
return m_sdkManagerToolArgs;
}
void AndroidConfig::setSdkManagerToolArgs(const QStringList &args)
{
m_sdkManagerToolArgs = args;
}
FileName AndroidConfig::ndkLocation() const
{
return m_ndkLocation;
@@ -836,7 +808,6 @@ FileName AndroidConfig::openJDKLocation() const
void AndroidConfig::setOpenJDKLocation(const FileName &openJDKLocation)
{
m_openJDKLocation = openJDKLocation;
m_availableSdkPlatformsUpToDate = false;
}
FileName AndroidConfig::keystoreLocation() const
@@ -1129,6 +1100,11 @@ const AndroidConfig &AndroidConfigurations::currentConfig()
return m_instance->m_config; // ensure that m_instance is initialized
}
AndroidSdkManager *AndroidConfigurations::sdkManager()
{
return m_instance->m_sdkManager.get();
}
AndroidConfigurations *AndroidConfigurations::instance()
{
return m_instance;
@@ -1143,7 +1119,8 @@ void AndroidConfigurations::save()
}
AndroidConfigurations::AndroidConfigurations(QObject *parent)
: QObject(parent)
: QObject(parent),
m_sdkManager(new AndroidSdkManager(m_config))
{
load();
@@ -1155,6 +1132,11 @@ AndroidConfigurations::AndroidConfigurations(QObject *parent)
m_instance = this;
}
AndroidConfigurations::~AndroidConfigurations()
{
}
static FileName javaHomeForJavac(const FileName &location)
{
QFileInfo fileInfo = location.toFileInfo();
@@ -1265,13 +1247,4 @@ void AndroidConfigurations::updateAndroidDevice()
AndroidConfigurations *AndroidConfigurations::m_instance = 0;
bool SdkPlatform::operator <(const SdkPlatform &other) const
{
if (apiLevel != other.apiLevel)
return apiLevel > other.apiLevel;
if (name != other.name)
return name < other.name;
return false;
}
} // namespace Android

View File

@@ -26,7 +26,7 @@
#pragma once
#include "android_global.h"
#include "androidsdkpackage.h"
#include <projectexplorer/toolchain.h>
#include <QObject>
@@ -52,7 +52,9 @@ class Project;
namespace Utils { class Environment; }
namespace Android {
class AndroidPlugin;
namespace Internal { class AndroidSdkManager; }
class AndroidDeviceInfo
{
@@ -74,31 +76,16 @@ public:
};
using AndroidDeviceInfoList = QList<AndroidDeviceInfo>;
//! Defines an Android system image.
class SystemImage
class CreateAvdInfo
{
public:
bool isValid() const { return (apiLevel != -1) && !abiName.isEmpty(); }
int apiLevel = -1;
QString abiName;
QString package;
Utils::FileName installedLocation;
};
using SystemImageList = QList<SystemImage>;
class SdkPlatform
{
public:
bool isValid() const { return !name.isEmpty() && apiLevel != -1; }
bool operator <(const SdkPlatform &other) const;
int apiLevel = -1;
bool isValid() const { return sdkPlatform && sdkPlatform->isValid() && !name.isEmpty(); }
const SdkPlatform *sdkPlatform = nullptr;
QString name;
QString package;
Utils::FileName installedLocation;
SystemImageList systemImages;
QString abi;
int sdcardSize = 0;
QString error; // only used in the return value of createAVD
};
using SdkPlatformList = QList<SdkPlatform>;
class ANDROID_EXPORT AndroidConfig
{
@@ -106,14 +93,15 @@ public:
void load(const QSettings &settings);
void save(QSettings &settings) const;
static QStringList apiLevelNamesFor(const QList<SdkPlatform> &platforms);
static QString apiLevelNameFor(const SdkPlatform &platform);
QList<SdkPlatform> sdkTargets(int minApiLevel = 0) const;
static QStringList apiLevelNamesFor(const SdkPlatformList &platforms);
static QString apiLevelNameFor(const SdkPlatform *platform);
Utils::FileName sdkLocation() const;
void setSdkLocation(const Utils::FileName &sdkLocation);
QVersionNumber sdkToolsVersion() const;
QVersionNumber buildToolsVersion() const;
QStringList sdkManagerToolArgs() const;
void setSdkManagerToolArgs(const QStringList &args);
Utils::FileName ndkLocation() const;
QVersionNumber ndkVersion() const;
@@ -147,19 +135,6 @@ public:
Utils::FileName keytoolPath() const;
class CreateAvdInfo
{
public:
bool isValid() const { return target.isValid() && !name.isEmpty(); }
SdkPlatform target;
QString name;
QString abi;
int sdcardSize = 0;
QString error; // only used in the return value of createAVD
};
CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, int minApiLevel = 0, QString targetArch = QString()) const;
QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const;
static QVector<AndroidDeviceInfo> connectedDevices(const QString &adbToolPath, QString *error = 0);
@@ -175,7 +150,8 @@ public:
OpenGl getOpenGLEnabled(const QString &emulator) const;
bool isConnected(const QString &serialNumber) const;
SdkPlatform highestAndroidSdk() const;
bool useNativeUiTools() const;
private:
static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property);
@@ -189,10 +165,10 @@ private:
bool isBootToQt(const QString &device) const;
static QString getAvdName(const QString &serialnumber);
void updateAvailableSdkPlatforms() const;
void updateNdkInformation() const;
Utils::FileName m_sdkLocation;
QStringList m_sdkManagerToolArgs;
Utils::FileName m_ndkLocation;
Utils::FileName m_openJDKLocation;
Utils::FileName m_keystoreLocation;
@@ -201,9 +177,6 @@ private:
bool m_automaticKitCreation = true;
//caches
mutable bool m_availableSdkPlatformsUpToDate = false;
mutable SdkPlatformList m_availableSdkPlatforms;
mutable bool m_NdkInformationUpToDate = false;
mutable QString m_toolchainHost;
mutable QVector<int> m_availableNdkPlatforms;
@@ -218,6 +191,7 @@ class ANDROID_EXPORT AndroidConfigurations : public QObject
public:
static const AndroidConfig &currentConfig();
static Internal::AndroidSdkManager *sdkManager();
static void setConfig(const AndroidConfig &config);
static AndroidConfigurations *instance();
@@ -236,16 +210,17 @@ signals:
private:
AndroidConfigurations(QObject *parent);
~AndroidConfigurations();
void load();
void save();
static AndroidConfigurations *m_instance;
AndroidConfig m_config;
std::unique_ptr<Internal::AndroidSdkManager> m_sdkManager;
QMap<ProjectExplorer::Project *, QMap<QString, QString> > m_defaultDeviceForAbi;
bool m_force32bit;
};
} // namespace Android
Q_DECLARE_METATYPE(Android::SdkPlatform)

View File

@@ -26,6 +26,7 @@
#include "androiddevicedialog.h"
#include "androidmanager.h"
#include "androidavdmanager.h"
#include "avddialog.h"
#include "ui_androiddevicedialog.h"
#include <utils/environment.h>
@@ -580,9 +581,10 @@ void AndroidDeviceDialog::devicesRefreshed()
void AndroidDeviceDialog::createAvd()
{
m_ui->createAVDButton->setEnabled(false);
AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi);
CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, AndroidConfigurations::sdkManager(),
m_apiLevel, m_abi);
if (!info.target.isValid()) {
if (!info.isValid()) {
m_ui->createAVDButton->setEnabled(true);
return;
}
@@ -593,7 +595,7 @@ void AndroidDeviceDialog::createAvd()
void AndroidDeviceDialog::avdAdded()
{
m_ui->createAVDButton->setEnabled(true);
AndroidConfig::CreateAvdInfo info = m_futureWatcherAddDevice.result();
CreateAvdInfo info = m_futureWatcherAddDevice.result();
if (!info.error.isEmpty()) {
QMessageBox::critical(this, QApplication::translate("AndroidConfig", "Error Creating AVD"), info.error);
return;

View File

@@ -79,7 +79,7 @@ private:
QString m_defaultDevice;
std::unique_ptr<AndroidAvdManager> m_avdManager;
QVector<AndroidDeviceInfo> m_connectedDevices;
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
QFutureWatcher<CreateAvdInfo> m_futureWatcherAddDevice;
QFutureWatcher<AndroidDeviceInfoList> m_futureWatcherRefreshDevices;
};

View File

@@ -35,6 +35,7 @@
#include "androidqtversion.h"
#include "androidbuildapkstep.h"
#include "androidavdmanager.h"
#include "androidsdkmanager.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/messagemanager.h>
@@ -174,7 +175,8 @@ QString AndroidManager::buildTargetSDK(ProjectExplorer::Target *target)
if (androidBuildApkStep)
return androidBuildApkStep->buildTargetSdk();
QString fallback = AndroidConfig::apiLevelNameFor(AndroidConfigurations::currentConfig().highestAndroidSdk());
QString fallback = AndroidConfig::apiLevelNameFor(
AndroidConfigurations::sdkManager()->latestAndroidSdkPlatform());
return fallback;
}

File diff suppressed because it is too large Load Diff

View File

@@ -25,26 +25,75 @@
#pragma once
#include "utils/fileutils.h"
#include "androidconfigurations.h"
#include "androidsdkpackage.h"
#include <QObject>
#include <QFuture>
#include <memory>
namespace Android {
class AndroidConfig;
namespace Internal {
class SdkManagerOutputParser;
class AndroidSdkManagerPrivate;
class AndroidSdkManager
class AndroidSdkManager : public QObject
{
Q_OBJECT
public:
AndroidSdkManager(const AndroidConfig &config);
enum CommandType
{
None,
UpdateAll,
UpdatePackage,
LicenseCheck,
LicenseWorkflow
};
struct OperationOutput
{
bool success = false;
CommandType type = None;
QString stdOutput;
QString stdError;
};
AndroidSdkManager(const AndroidConfig &config, QObject *parent = nullptr);
~AndroidSdkManager();
SdkPlatformList availableSdkPlatforms(bool *ok = nullptr);
SdkPlatformList installedSdkPlatforms();
const AndroidSdkPackageList &allSdkPackages();
AndroidSdkPackageList availableSdkPackages();
AndroidSdkPackageList installedSdkPackages();
SdkPlatform *latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state
= AndroidSdkPackage::Installed);
SdkPlatformList filteredSdkPlatforms(int minApiLevel,
AndroidSdkPackage::PackageState state
= AndroidSdkPackage::Installed);
void reloadPackages(bool forceReload = false);
bool isBusy() const;
QFuture<QString> availableArguments() const;
QFuture<OperationOutput> updateAll();
QFuture<OperationOutput> update(const QStringList &install, const QStringList &uninstall);
QFuture<OperationOutput> checkPendingLicenses();
QFuture<OperationOutput> runLicenseCommand();
void cancelOperatons();
void acceptSdkLicense(bool accept);
signals:
void packageReloadBegin();
void packageReloadFinished();
void cancelActiveOperations();
private:
const AndroidConfig &m_config;
std::unique_ptr<SdkManagerOutputParser> m_parser;
std::unique_ptr<AndroidSdkManagerPrivate> m_d;
friend class AndroidSdkManagerPrivate;
};
} // namespace Internal

View File

@@ -0,0 +1,579 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "androidsdkmanagerwidget.h"
#include "ui_androidsdkmanagerwidget.h"
#include "androidconfigurations.h"
#include "androidsdkmanager.h"
#include "androidsdkmodel.h"
#include "utils/runextensions.h"
#include "utils/outputformatter.h"
#include "utils/runextensions.h"
#include "utils/qtcassert.h"
#include "utils/utilsicons.h"
#include <QDialogButtonBox>
#include <QLabel>
#include <QLineEdit>
#include <QLoggingCategory>
#include <QMessageBox>
#include <QProcess>
#include <QSortFilterProxyModel>
namespace {
Q_LOGGING_CATEGORY(androidSdkMgrUiLog, "qtc.android.sdkManagerUi")
}
namespace Android {
namespace Internal {
using namespace std::placeholders;
class OptionsDialog : public QDialog
{
public:
OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &args,
QWidget *parent = nullptr);
~OptionsDialog();
QStringList sdkManagerArguments() const;
private:
QPlainTextEdit *argumentDetailsEdit;
QLineEdit *argumentsEdit;
QFuture<QString> m_optionsFuture;
};
class PackageFilterModel : public QSortFilterProxyModel
{
public:
PackageFilterModel(AndroidSdkModel* sdkModel);
void setAcceptedPackageState(AndroidSdkPackage::PackageState state);
bool filterAcceptsRow(int source_row, const QModelIndex &sourceParent) const override;
private:
AndroidSdkPackage::PackageState m_packageState = AndroidSdkPackage::AnyValidState;
};
AndroidSdkManagerWidget::AndroidSdkManagerWidget(AndroidConfig &config,
AndroidSdkManager *sdkManager, QWidget *parent) :
QWidget(parent),
m_androidConfig(config),
m_sdkManager(sdkManager),
m_sdkModel(new AndroidSdkModel(m_androidConfig, m_sdkManager, this)),
m_ui(new Ui::AndroidSdkManagerWidget)
{
QTC_CHECK(sdkManager);
m_ui->setupUi(this);
m_ui->sdkLicensebuttonBox->hide();
m_ui->sdkLicenseLabel->hide();
m_ui->warningLabel->setElideMode(Qt::ElideRight);
m_ui->warningIconLabel->setPixmap(Utils::Icons::WARNING.pixmap());
m_ui->viewStack->setCurrentWidget(m_ui->packagesStack);
m_formatter = new Utils::OutputFormatter;
m_formatter->setPlainTextEdit(m_ui->outputEdit);
connect(m_sdkModel, &AndroidSdkModel::dataChanged, [this]() {
if (m_ui->viewStack->currentWidget() == m_ui->packagesStack)
m_ui->applySelectionButton->setEnabled(!m_sdkModel->userSelection().isEmpty());
});
connect(m_sdkModel, &AndroidSdkModel::modelAboutToBeReset, [this]() {
m_ui->applySelectionButton->setEnabled(false);
m_ui->expandCheck->setChecked(false);
cancelPendingOperations();
switchView(PackageListing);
});
auto proxyModel = new PackageFilterModel(m_sdkModel);
m_ui->packagesView->setModel(proxyModel);
m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::packageNameColumn,
QHeaderView::ResizeToContents);
m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::apiLevelColumn,
QHeaderView::ResizeToContents);
m_ui->packagesView->header()->setSectionResizeMode(AndroidSdkModel::packageRevisionColumn,
QHeaderView::ResizeToContents);
connect(m_ui->expandCheck, &QCheckBox::stateChanged, [this](int state) {
if (state == Qt::Checked)
m_ui->packagesView->expandAll();
else
m_ui->packagesView->collapseAll();
});
connect(m_ui->updateInstalledButton, &QPushButton::clicked,
this, &AndroidSdkManagerWidget::onUpdatePackages);
connect(m_ui->showAllRadio, &QRadioButton::toggled, [this, proxyModel](bool checked) {
if (checked) {
proxyModel->setAcceptedPackageState(AndroidSdkPackage::AnyValidState);
m_sdkModel->resetSelection();
}
});
connect(m_ui->showInstalledRadio, &QRadioButton::toggled, [this, proxyModel](bool checked) {
if (checked) {
proxyModel->setAcceptedPackageState(AndroidSdkPackage::Installed);
m_sdkModel->resetSelection();
}
});
connect(m_ui->showAvailableRadio, &QRadioButton::toggled, [this, proxyModel](bool checked) {
if (checked) {
proxyModel->setAcceptedPackageState(AndroidSdkPackage::Available);
m_sdkModel->resetSelection();
}
});
connect(m_ui->applySelectionButton, &QPushButton::clicked,
this, &AndroidSdkManagerWidget::onApplyButton);
connect(m_ui->cancelButton, &QPushButton::clicked, this,
&AndroidSdkManagerWidget::onCancel);
connect(m_ui->nativeSdkManagerButton, &QPushButton::clicked,
this, &AndroidSdkManagerWidget::onNativeSdkManager);
connect(m_ui->optionsButton, &QPushButton::clicked,
this, &AndroidSdkManagerWidget::onSdkManagerOptions);
connect(m_ui->sdkLicensebuttonBox, &QDialogButtonBox::accepted, [this]() {
m_sdkManager->acceptSdkLicense(true);
m_ui->sdkLicensebuttonBox->setEnabled(false); // Wait for next license to enable controls
});
connect(m_ui->sdkLicensebuttonBox, &QDialogButtonBox::rejected, [this]() {
m_sdkManager->acceptSdkLicense(false);
m_ui->sdkLicensebuttonBox->setEnabled(false); // Wait for next license to enable controls
});
}
AndroidSdkManagerWidget::~AndroidSdkManagerWidget()
{
if (m_currentOperation)
delete m_currentOperation;
cancelPendingOperations();
delete m_formatter;
delete m_ui;
}
void AndroidSdkManagerWidget::setSdkManagerControlsEnabled(bool enable)
{
m_ui->packagesTypeGroup->setEnabled(enable);
m_ui->expandCheck->setVisible(enable);
m_ui->warningIconLabel->setVisible(!enable);
m_ui->warningLabel->setVisible(!enable);
m_ui->packagesView->setEnabled(enable);
m_ui->updateInstalledButton->setEnabled(enable);
m_ui->optionsButton->setEnabled(enable);
}
void AndroidSdkManagerWidget::installEssentials()
{
m_sdkModel->selectMissingEssentials();
m_ui->applySelectionButton->click();
}
void AndroidSdkManagerWidget::beginLicenseCheck()
{
m_formatter->appendMessage(tr("Checking pending licenses...\n"), Utils::NormalMessageFormat);
addPackageFuture(m_sdkManager->checkPendingLicenses());
}
void AndroidSdkManagerWidget::onApplyButton()
{
QTC_ASSERT(m_currentView == PackageListing, return);
if (m_sdkManager->isBusy()) {
m_formatter->appendMessage(tr("\nSDK Manager is busy."), Utils::StdErrFormat);
return;
}
const QList<const AndroidSdkPackage *> packagesToUpdate = m_sdkModel->userSelection();
if (packagesToUpdate.isEmpty())
return;
QStringList installPackages, uninstallPackages;
for (auto package : packagesToUpdate) {
QString str = QString(" %1").arg(package->descriptionText());
if (package->state() == AndroidSdkPackage::Installed)
uninstallPackages << str;
else
installPackages << str;
}
QMessageBox messageDlg(QMessageBox::Information, tr("Android SDK Changes"),
tr("%n Android SDK packages shall be updated.",
"", packagesToUpdate.count()),
QMessageBox::Ok | QMessageBox::Cancel, this);
QString details;
if (!uninstallPackages.isEmpty())
details = tr("[Packages to be uninstalled:]\n").append(uninstallPackages.join("\n"));
if (!installPackages.isEmpty()) {
if (!uninstallPackages.isEmpty())
details.append("\n\n");
details.append("[Packages to be installed:]\n").append(installPackages.join("\n"));
}
messageDlg.setDetailedText(details);
if (messageDlg.exec() == QMessageBox::Cancel)
return;
switchView(Operations);
m_pendingCommand = AndroidSdkManager::UpdatePackage;
// User agreed with the selection. Check for licenses.
if (!installPackages.isEmpty()) {
// Pending license affects installtion only.
beginLicenseCheck();
} else {
// Uninstall only. Go Ahead.
beginExecution();
}
}
void AndroidSdkManagerWidget::onUpdatePackages()
{
if (m_sdkManager->isBusy()) {
m_formatter->appendMessage(tr("\nSDK Manager is busy."), Utils::StdErrFormat);
return;
}
switchView(Operations);
m_pendingCommand = AndroidSdkManager::UpdateAll;
beginLicenseCheck();
}
void AndroidSdkManagerWidget::onCancel()
{
cancelPendingOperations();
}
void AndroidSdkManagerWidget::onNativeSdkManager()
{
if (m_androidConfig.useNativeUiTools()) {
QProcess::startDetached(m_androidConfig.androidToolPath().toString());
} else {
QMessageBox::warning(this, tr("Native SDK Manager Not Available"),
tr("SDK manager UI tool is not available in the installed SDK tools"
"(version %1). Use the command line tool \"sdkmanager\" for "
"advanced SDK management.")
.arg(m_androidConfig.sdkToolsVersion().toString()));
}
}
void AndroidSdkManagerWidget::onOperationResult(int index)
{
QTC_ASSERT(m_currentOperation, return);
AndroidSdkManager::OperationOutput result = m_currentOperation->resultAt(index);
if (result.type == AndroidSdkManager::LicenseWorkflow) {
// Show license controls and enable to user input.
m_ui->sdkLicenseLabel->setVisible(true);
m_ui->sdkLicensebuttonBox->setVisible(true);
m_ui->sdkLicensebuttonBox->setEnabled(true);
m_ui->sdkLicensebuttonBox->button(QDialogButtonBox::No)->setDefault(true);
}
auto breakLine = [](const QString &line) { return line.endsWith("\n") ? line : line + "\n";};
if (!result.stdError.isEmpty() && result.type != AndroidSdkManager::LicenseCheck)
m_formatter->appendMessage(breakLine(result.stdError), Utils::StdErrFormat);
if (!result.stdOutput.isEmpty() && result.type != AndroidSdkManager::LicenseCheck)
m_formatter->appendMessage(breakLine(result.stdOutput), Utils::StdOutFormat);
m_ui->outputEdit->ensureCursorVisible();
}
void AndroidSdkManagerWidget::onLicenseCheckResult(const AndroidSdkManager::OperationOutput& output)
{
if (output.success) {
// No assertion was found. Looks like all license are accepted. Go Ahead.
runPendingCommand();
} else {
// Assertion was found. Provide user workflow to accept licenses.
QString warningMessage = tr("\nPlease note that the installation and use of Android SDK "
"packages may fail if respective licenses are not accepted.");
int userSelection = QMessageBox::question(this, tr("Android SDK Licenses"),
output.stdOutput + warningMessage,
QMessageBox::Yes | QMessageBox::No);
if (userSelection == QMessageBox::Yes) {
// Run license workflow.
beginLicenseWorkflow();
} else {
// User decided to go ahead anyways.
runPendingCommand();
}
}
}
void AndroidSdkManagerWidget::addPackageFuture(const QFuture<AndroidSdkManager::OperationOutput>
&future)
{
QTC_ASSERT(!m_currentOperation, return);
if (!future.isFinished() || !future.isCanceled()) {
m_currentOperation = new QFutureWatcher<AndroidSdkManager::OperationOutput>;
m_currentOperation->setFuture(future);
connect(m_currentOperation,
&QFutureWatcher<AndroidSdkManager::OperationOutput>::resultReadyAt,
this, &AndroidSdkManagerWidget::onOperationResult);
connect(m_currentOperation, &QFutureWatcher<AndroidSdkManager::OperationOutput>::finished,
this, &AndroidSdkManagerWidget::packageFutureFinished);
connect(m_currentOperation,
&QFutureWatcher<AndroidSdkManager::OperationOutput>::progressValueChanged,
[this](int value) {
m_ui->operationProgress->setValue(value);
});
} else {
qCDebug(androidSdkMgrUiLog) << "Operation canceled/finished before adding to the queue";
if (m_sdkManager->isBusy()) {
m_formatter->appendMessage(tr("SDK Manager is busy. Operation cancelled."),
Utils::StdErrFormat);
}
notifyOperationFinished();
switchView(PackageListing);
}
}
void AndroidSdkManagerWidget::beginExecution()
{
const QList<const AndroidSdkPackage *> packagesToUpdate = m_sdkModel->userSelection();
if (packagesToUpdate.isEmpty()) {
switchView(PackageListing);
return;
}
QStringList installSdkPaths, uninstallSdkPaths;
for (auto package : packagesToUpdate) {
if (package->state() == AndroidSdkPackage::Installed)
uninstallSdkPaths << package->sdkStylePath();
else
installSdkPaths << package->sdkStylePath();
}
m_formatter->appendMessage(tr("Installing/Uninstalling selected packages...\n"),
Utils::NormalMessageFormat);
m_formatter->appendMessage(tr("Closing the %1 dialog will cancel the running and scheduled SDK "
"operations.\n").arg(Utils::HostOsInfo::isMacHost() ?
tr("preferences") : tr("options")),
Utils::LogMessageFormat);
addPackageFuture(m_sdkManager->update(installSdkPaths, uninstallSdkPaths));
}
void AndroidSdkManagerWidget::beginUpdate()
{
m_formatter->appendMessage(tr("Updating installed packages...\n"), Utils::NormalMessageFormat);
m_formatter->appendMessage(tr("Closing the %1 dialog will cancel the running and scheduled SDK "
"operations.\n").arg(Utils::HostOsInfo::isMacHost() ?
tr("preferences") : tr("options")),
Utils::LogMessageFormat);
addPackageFuture(m_sdkManager->updateAll());
}
void AndroidSdkManagerWidget::beginLicenseWorkflow()
{
switchView(LicenseWorkflow);
addPackageFuture(m_sdkManager->runLicenseCommand());
}
void AndroidSdkManagerWidget::notifyOperationFinished()
{
if (!m_currentOperation || m_currentOperation->isFinished()) {
QMessageBox::information(this, tr("Android SDK Changes"),
tr("Android SDK operations finished."), QMessageBox::Ok);
m_ui->operationProgress->setValue(0);
}
}
void AndroidSdkManagerWidget::packageFutureFinished()
{
QTC_ASSERT (m_currentOperation, return);
bool continueWorkflow = true;
if (m_currentOperation->isCanceled()) {
m_formatter->appendMessage(tr("Operation cancelled.\n"), Utils::StdErrFormat);
continueWorkflow = false;
}
m_ui->operationProgress->setValue(100);
int resultCount = m_currentOperation->future().resultCount();
if (continueWorkflow && resultCount > 0) {
AndroidSdkManager::OperationOutput output = m_currentOperation->resultAt(resultCount -1);
AndroidSdkManager::CommandType type = output.type;
m_currentOperation->deleteLater();
m_currentOperation = nullptr;
switch (type) {
case AndroidSdkManager::LicenseCheck:
onLicenseCheckResult(output);
break;
case AndroidSdkManager::LicenseWorkflow:
m_ui->sdkLicensebuttonBox->hide();
m_ui->sdkLicenseLabel->hide();
runPendingCommand();
break;
case AndroidSdkManager::UpdateAll:
case AndroidSdkManager::UpdatePackage:
notifyOperationFinished();
switchView(PackageListing);
m_sdkManager->reloadPackages(true);
break;
default:
break;
}
} else {
m_currentOperation->deleteLater();
m_currentOperation = nullptr;
switchView(PackageListing);
m_sdkManager->reloadPackages(true);
}
}
void AndroidSdkManagerWidget::cancelPendingOperations()
{
if (!m_sdkManager->isBusy()) {
m_formatter->appendMessage(tr("\nNo pending operations to cancel...\n"),
Utils::NormalMessageFormat);
switchView(PackageListing);
return;
}
m_formatter->appendMessage(tr("\nCancelling pending operations...\n"),
Utils::NormalMessageFormat);
m_sdkManager->cancelOperatons();
}
void AndroidSdkManagerWidget::switchView(AndroidSdkManagerWidget::View view)
{
if (m_currentView == PackageListing) {
m_formatter->clear();
m_ui->outputEdit->clear();
}
m_currentView = view;
if (m_currentView == PackageListing)
emit updatingSdkFinished();
else
emit updatingSdk();
m_ui->operationProgress->setValue(0);
m_ui->viewStack->setCurrentWidget(m_currentView == PackageListing ?
m_ui->packagesStack : m_ui->outputStack);
}
void AndroidSdkManagerWidget::runPendingCommand()
{
if (m_pendingCommand == AndroidSdkManager::UpdatePackage)
beginExecution(); // License workflow can only start when updating packages.
else if (m_pendingCommand == AndroidSdkManager::UpdateAll)
beginUpdate();
else
QTC_ASSERT(false, qCDebug(androidSdkMgrUiLog) << "Unexpected state: No pending command.");
}
void AndroidSdkManagerWidget::onSdkManagerOptions()
{
OptionsDialog dlg(m_sdkManager, m_androidConfig.sdkManagerToolArgs(), this);
if (dlg.exec() == QDialog::Accepted) {
QStringList arguments = dlg.sdkManagerArguments();
if (arguments != m_androidConfig.sdkManagerToolArgs()) {
m_androidConfig.setSdkManagerToolArgs(arguments);
m_sdkManager->reloadPackages(true);
}
}
}
PackageFilterModel::PackageFilterModel(AndroidSdkModel *sdkModel) :
QSortFilterProxyModel(sdkModel)
{
setSourceModel(sdkModel);
}
void PackageFilterModel::setAcceptedPackageState(AndroidSdkPackage::PackageState state)
{
m_packageState = state;
invalidateFilter();
}
bool PackageFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
{
QModelIndex srcIndex = sourceModel()->index(sourceRow, 0, sourceParent);
if (!srcIndex.isValid())
return false;
auto packageState = [](const QModelIndex& i) {
return (AndroidSdkPackage::PackageState)i.data(AndroidSdkModel::PackageStateRole).toInt();
};
bool showTopLevel = false;
if (!sourceParent.isValid()) {
// Top Level items
for (int row = 0; row < sourceModel()->rowCount(srcIndex); ++row) {
QModelIndex childIndex = sourceModel()->index(row, 0, srcIndex);
if (m_packageState & packageState(childIndex)) {
showTopLevel = true;
break;
}
}
}
return showTopLevel || (packageState(srcIndex) & m_packageState);
}
OptionsDialog::OptionsDialog(AndroidSdkManager *sdkManager, const QStringList &args,
QWidget *parent) : QDialog(parent)
{
QTC_CHECK(sdkManager);
resize(800, 480);
setWindowTitle(tr("SDK Manager Arguments"));
argumentDetailsEdit = new QPlainTextEdit(this);
argumentDetailsEdit->setReadOnly(true);
auto populateOptions = [this](const QString& options) {
if (options.isEmpty()) {
argumentDetailsEdit->setPlainText(tr("Cannot load available arguments for "
"\"sdkmanager\" command."));
} else {
argumentDetailsEdit->setPlainText(options);
}
};
m_optionsFuture = sdkManager->availableArguments();
Utils::onResultReady(m_optionsFuture, populateOptions);
auto dialogButtons = new QDialogButtonBox(this);
dialogButtons->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok);
connect(dialogButtons, &QDialogButtonBox::accepted, this, &OptionsDialog::accept);
connect(dialogButtons, &QDialogButtonBox::rejected, this, &OptionsDialog::reject);
argumentsEdit = new QLineEdit(this);
argumentsEdit->setText(args.join(" "));
auto gridLayout = new QGridLayout(this);
gridLayout->addWidget(new QLabel(tr("SDK manager arguments:"), this), 0, 0, 1, 1);
gridLayout->addWidget(argumentsEdit, 0, 1, 1, 1);
gridLayout->addWidget(new QLabel(tr("Available arguments:"), this), 1, 0, 1, 2);
gridLayout->addWidget(argumentDetailsEdit, 2, 0, 1, 2);
gridLayout->addWidget(dialogButtons, 3, 0, 1, 2);
}
OptionsDialog::~OptionsDialog()
{
m_optionsFuture.cancel();
m_optionsFuture.waitForFinished();
}
QStringList OptionsDialog::sdkManagerArguments() const
{
QString userInput = argumentsEdit->text().simplified();
return userInput.isEmpty() ? QStringList() : userInput.split(' ');
}
} // namespace Internal
} // namespace Android

View File

@@ -0,0 +1,97 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "androidconfigurations.h"
#include "androidsdkmanager.h"
#include <QWidget>
#include <QFutureWatcher>
namespace Utils { class OutputFormatter; }
namespace Android {
namespace Internal {
class AndroidSdkManager;
namespace Ui {
class AndroidSdkManagerWidget;
}
class AndroidSdkModel;
class AndroidSdkManagerWidget : public QWidget
{
Q_OBJECT
enum View {
PackageListing,
Operations,
LicenseWorkflow
};
public:
AndroidSdkManagerWidget(AndroidConfig &config, AndroidSdkManager *sdkManager,
QWidget *parent = nullptr);
~AndroidSdkManagerWidget();
void setSdkManagerControlsEnabled(bool enable);
void installEssentials();
signals:
void updatingSdk();
void updatingSdkFinished();
private:
void onApplyButton();
void onUpdatePackages();
void onCancel();
void onNativeSdkManager();
void onOperationResult(int index);
void onLicenseCheckResult(const AndroidSdkManager::OperationOutput &output);
void onSdkManagerOptions();
void addPackageFuture(const QFuture<AndroidSdkManager::OperationOutput> &future);
void beginLicenseCheck();
void beginExecution();
void beginUpdate();
void beginLicenseWorkflow();
void notifyOperationFinished();
void packageFutureFinished();
void cancelPendingOperations();
void switchView(View view);
void runPendingCommand();
AndroidConfig &m_androidConfig;
AndroidSdkManager::CommandType m_pendingCommand = AndroidSdkManager::None;
View m_currentView = PackageListing;
AndroidSdkManager *m_sdkManager = nullptr;
AndroidSdkModel *m_sdkModel = nullptr;
Ui::AndroidSdkManagerWidget *m_ui = nullptr;
Utils::OutputFormatter *m_formatter = nullptr;
QFutureWatcher<AndroidSdkManager::OperationOutput> *m_currentOperation = nullptr;
};
} // namespace Internal
} // namespace Android

View File

@@ -0,0 +1,311 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Android::Internal::AndroidSdkManagerWidget</class>
<widget class="QWidget" name="Android::Internal::AndroidSdkManagerWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>664</width>
<height>396</height>
</rect>
</property>
<property name="windowTitle">
<string>Android SDK Manager</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_3">
<property name="spacing">
<number>-1</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QStackedWidget" name="viewStack">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="packagesStack">
<layout class="QGridLayout" name="gridLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="spacing">
<number>4</number>
</property>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QCheckBox" name="expandCheck">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Expand All</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="warningIconLabel">
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="Utils::ElidingLabel" name="warningLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>SDK manger is not available with the current version of SDK tools. Use native SDK manager.</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="1" column="0">
<widget class="QTreeView" name="packagesView">
<property name="indentation">
<number>20</number>
</property>
<attribute name="headerCascadingSectionResizes">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="1" column="1">
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QPushButton" name="updateInstalledButton">
<property name="text">
<string>Update Installed</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="applySelectionButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QGroupBox" name="packagesTypeGroup">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="title">
<string>Show Packages</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>12</number>
</property>
<item>
<widget class="QRadioButton" name="showAvailableRadio">
<property name="text">
<string>Available</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="showInstalledRadio">
<property name="text">
<string>Installed</string>
</property>
</widget>
</item>
<item>
<widget class="QRadioButton" name="showAllRadio">
<property name="text">
<string>All</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item>
<widget class="QPushButton" name="optionsButton">
<property name="text">
<string>Advanced Options...</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>5</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="nativeSdkManagerButton">
<property name="text">
<string>Native SDK Manager...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="outputStack">
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="2">
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="QProgressBar" name="operationProgress">
<property name="maximum">
<number>100</number>
</property>
<property name="value">
<number>0</number>
</property>
<property name="textVisible">
<bool>true</bool>
</property>
<property name="invertedAppearance">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="0" column="0" colspan="3">
<widget class="QPlainTextEdit" name="outputEdit">
<property name="readOnly">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="sdkLicenseLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Do you want to accept the Android SDK license?</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="1" colspan="2">
<widget class="QDialogButtonBox" name="sdkLicensebuttonBox">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::No|QDialogButtonBox::Yes</set>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>Utils::ElidingLabel</class>
<extends>QLabel</extends>
<header>utils/elidinglabel.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>packagesView</tabstop>
<tabstop>showAllRadio</tabstop>
<tabstop>showInstalledRadio</tabstop>
<tabstop>showAvailableRadio</tabstop>
<tabstop>outputEdit</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@@ -0,0 +1,352 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "androidsdkmodel.h"
#include "androidmanager.h"
#include "androidsdkmanager.h"
#include "utils/algorithm.h"
#include "utils/qtcassert.h"
#include "utils/utilsicons.h"
#include <QIcon>
namespace Android {
namespace Internal {
const int packageColCount = 4;
AndroidSdkModel::AndroidSdkModel(const AndroidConfig &config, AndroidSdkManager *sdkManager,
QObject *parent)
: QAbstractItemModel(parent),
m_config(config),
m_sdkManager(sdkManager)
{
QTC_CHECK(m_sdkManager);
connect(m_sdkManager, &AndroidSdkManager::packageReloadBegin, [this]() {
clearContainers();
beginResetModel();
});
connect(m_sdkManager, &AndroidSdkManager::packageReloadFinished, [this]() {
refreshData();
endResetModel();
});
}
QVariant AndroidSdkModel::headerData(int section, Qt::Orientation orientation, int role) const
{
Q_UNUSED(orientation)
QVariant data;
if (role == Qt::DisplayRole) {
switch (section) {
case packageNameColumn:
data = tr("Package");
break;
case packageRevisionColumn:
data = tr("Revision");
break;
case apiLevelColumn:
data = tr("API");
break;
case operationColumn:
data = tr("Operation");
break;
default:
break;
}
}
return data;
}
QModelIndex AndroidSdkModel::index(int row, int column, const QModelIndex &parent) const
{
if (parent.isValid()) {
// Packages under top items.
if (parent.row() == 0) {
// Tools packages
if (row < m_tools.count())
return createIndex(row, column, const_cast<AndroidSdkPackage *>(m_tools.at(row)));
} else if (parent.row() < m_sdkPlatforms.count() + 1) {
// Platform packages
const SdkPlatform *sdkPlatform = m_sdkPlatforms.at(parent.row() - 1);
SystemImageList images = sdkPlatform->systemImages(AndroidSdkPackage::AnyValidState);
if (row < images.count() + 1) {
if (row == 0)
return createIndex(row, column, const_cast<SdkPlatform *>(sdkPlatform));
else
return createIndex(row, column, images.at(row - 1));
}
}
} else if (row < m_sdkPlatforms.count() + 1) {
return createIndex(row, column); // Top level items (Tools & platform)
}
return QModelIndex();
}
QModelIndex AndroidSdkModel::parent(const QModelIndex &index) const
{
void *ip = index.internalPointer();
if (!ip)
return QModelIndex();
auto package = static_cast<const AndroidSdkPackage *>(ip);
if (package->type() == AndroidSdkPackage::SystemImagePackage) {
auto image = static_cast<const SystemImage *>(package);
int row = m_sdkPlatforms.indexOf(const_cast<SdkPlatform *>(image->platform()));
if (row > -1)
return createIndex(row + 1, 0);
} else if (package->type() == AndroidSdkPackage::SdkPlatformPackage) {
int row = m_sdkPlatforms.indexOf(static_cast<const SdkPlatform *>(package));
if (row > -1)
return createIndex(row + 1, 0);
} else {
return createIndex(0, 0); // Tools
}
return QModelIndex();
}
int AndroidSdkModel::rowCount(const QModelIndex &parent) const
{
if (!parent.isValid())
return m_sdkPlatforms.count() + 1;
if (!parent.internalPointer()) {
if (parent.row() == 0) // Tools
return m_tools.count();
if (parent.row() <= m_sdkPlatforms.count()) {
const SdkPlatform * platform = m_sdkPlatforms.at(parent.row() - 1);
return platform->systemImages(AndroidSdkPackage::AnyValidState).count() + 1;
}
}
return 0;
}
int AndroidSdkModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return packageColCount;
}
QVariant AndroidSdkModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return QVariant();
if (!index.parent().isValid()) {
// Top level tools
if (index.row() == 0) {
return role == Qt::DisplayRole && index.column() == packageNameColumn ?
QVariant(tr("Tools")) : QVariant();
}
// Top level platforms
const SdkPlatform *platform = m_sdkPlatforms.at(index.row() - 1);
if (role == Qt::DisplayRole) {
if (index.column() == packageNameColumn) {
QString androidName = AndroidManager::androidNameForApiLevel(platform->apiLevel());
if (androidName.startsWith("Android"))
return androidName;
else
return platform->displayText();
} else if (index.column() == apiLevelColumn) {
return platform->apiLevel();
}
}
return QVariant();
}
auto p = static_cast<const AndroidSdkPackage *>(index.internalPointer());
QString apiLevelStr;
if (p->type() == AndroidSdkPackage::SdkPlatformPackage)
apiLevelStr = QString::number(static_cast<const SdkPlatform *>(p)->apiLevel());
if (p->type() == AndroidSdkPackage::SystemImagePackage)
apiLevelStr = QString::number(static_cast<const SystemImage *>(p)->platform()->apiLevel());
if (role == Qt::DisplayRole) {
switch (index.column()) {
case packageNameColumn:
return p->type() == AndroidSdkPackage::SdkPlatformPackage ?
tr("SDK Platform") : p->displayText();
case packageRevisionColumn:
return p->revision().toString();
case apiLevelColumn:
return apiLevelStr;
case operationColumn:
if (p->type() == AndroidSdkPackage::SdkToolsPackage &&
p->state() == AndroidSdkPackage::Installed) {
return tr("Update Only");
} else {
return p->state() == AndroidSdkPackage::Installed ? tr("Uninstall") : tr("Install");
}
default:
break;
}
}
if (role == Qt::DecorationRole && index.column() == packageNameColumn) {
return p->state() == AndroidSdkPackage::Installed ? Utils::Icons::OK.icon() :
Utils::Icons::EMPTY16.icon();
}
if (role == Qt::CheckStateRole && index.column() == operationColumn )
return m_changeState.contains(p) ? Qt::Checked : Qt::Unchecked;
if (role == Qt::ToolTipRole)
return QString("%1 - (%2)").arg(p->descriptionText()).arg(p->sdkStylePath());
if (role == PackageTypeRole)
return p->type();
if (role == PackageStateRole)
return p->state();
return QVariant();
}
QHash<int, QByteArray> AndroidSdkModel::roleNames() const
{
QHash <int, QByteArray> roles;
roles[PackageTypeRole] = "PackageRole";
roles[PackageStateRole] = "PackageState";
return roles;
}
Qt::ItemFlags AndroidSdkModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags f = QAbstractItemModel::flags(index);
if (index.column() == operationColumn)
f |= Qt::ItemIsUserCheckable;
void *ip = index.internalPointer();
if (ip && index.column() == operationColumn) {
auto package = static_cast<const AndroidSdkPackage *>(ip);
if (package->state() == AndroidSdkPackage::Installed &&
package->type() == AndroidSdkPackage::SdkToolsPackage) {
f &= ~Qt::ItemIsEnabled;
}
}
return f;
}
bool AndroidSdkModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
void *ip = index.internalPointer();
if (ip && role == Qt::CheckStateRole) {
auto package = static_cast<const AndroidSdkPackage *>(ip);
if (value.toInt() == Qt::Checked) {
m_changeState << package;
emit dataChanged(index, index, {Qt::CheckStateRole});
} else if (m_changeState.remove(package)) {
emit dataChanged(index, index, {Qt::CheckStateRole});
}
return true;
}
return false;
}
void AndroidSdkModel::selectMissingEssentials()
{
resetSelection();
bool selectPlatformTool = !m_config.adbToolPath().exists();
bool selectBuildTools = m_config.buildToolsVersion().isNull();
auto addTool = [this](QList<const AndroidSdkPackage *>::const_iterator itr) {
m_changeState << *itr;
auto i = index(std::distance(m_tools.cbegin(), itr), 0, index(0, 0));
emit dataChanged(i, i, {Qt::CheckStateRole});
};
for (auto tool = m_tools.cbegin(); tool != m_tools.cend(); ++tool) {
if (selectPlatformTool && (*tool)->type() == AndroidSdkPackage::PlatformToolsPackage) {
// Select Platform tools
addTool(tool);
selectPlatformTool = false;
}
if (selectBuildTools && (*tool)->type() == AndroidSdkPackage::BuildToolsPackage) {
// Select build tools
addTool(tool);
selectBuildTools = false;
}
if (!selectPlatformTool && !selectBuildTools)
break;
}
// Select SDK platform
if (m_sdkManager->installedSdkPlatforms().isEmpty() && !m_sdkPlatforms.isEmpty()) {
auto i = index(0, 0, index(1,0));
m_changeState << m_sdkPlatforms.at(0);
emit dataChanged(i , i, {Qt::CheckStateRole});
}
}
QList<const AndroidSdkPackage *> AndroidSdkModel::userSelection() const
{
return m_changeState.toList();
}
void AndroidSdkModel::resetSelection()
{
beginResetModel();
m_changeState.clear();
endResetModel();
}
void AndroidSdkModel::clearContainers()
{
m_sdkPlatforms.clear();
m_tools.clear();
m_changeState.clear();
}
void AndroidSdkModel::refreshData()
{
clearContainers();
for (AndroidSdkPackage *p : m_sdkManager->allSdkPackages()) {
if (p->type() == AndroidSdkPackage::SdkPlatformPackage)
m_sdkPlatforms << static_cast<SdkPlatform *>(p);
else
m_tools << p;
}
Utils::sort(m_sdkPlatforms, [](const SdkPlatform *p1, const SdkPlatform *p2) {
return p1->apiLevel() > p2->apiLevel();
});
Utils::sort(m_tools, [](const AndroidSdkPackage *p1, const AndroidSdkPackage *p2) {
if (p1->state() == p2->state()) {
if (p1->type() == p2->type())
return p1->revision() > p2->revision();
else
return p1->type() > p2->type();
} else {
return p1->state() < p2->state();
}
});
}
} // namespace Internal
} // namespace Android

View File

@@ -0,0 +1,86 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "androidconfigurations.h"
#include <QAbstractItemModel>
#include <memory>
namespace Android {
namespace Internal {
class AndroidSdkManager;
class AndroidSdkModel : public QAbstractItemModel
{
Q_OBJECT
public:
enum PackageColumn {
packageNameColumn = 0,
apiLevelColumn,
packageRevisionColumn,
operationColumn
};
enum ExtraRoles {
PackageTypeRole = Qt::UserRole + 1,
PackageStateRole
};
explicit AndroidSdkModel(const AndroidConfig &config, AndroidSdkManager *sdkManager,
QObject *parent = 0);
// QAbstractItemModel overrides.
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &index) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
void selectMissingEssentials();
QList<const AndroidSdkPackage *> userSelection() const;
void resetSelection();
private:
void clearContainers();
void refreshData();
private:
const AndroidConfig &m_config;
AndroidSdkManager *m_sdkManager;
QList<const SdkPlatform *> m_sdkPlatforms;
QList<const AndroidSdkPackage *> m_tools;
QSet<const AndroidSdkPackage *> m_changeState;
};
} // namespace Internal
} // namespace Android

View File

@@ -0,0 +1,256 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "androidsdkpackage.h"
#include "utils/algorithm.h"
namespace Android {
AndroidSdkPackage::AndroidSdkPackage(QVersionNumber version, QString sdkStylePathStr,
QObject *parent) :
QObject(parent),
m_revision(version),
m_sdkStylePath(sdkStylePathStr)
{
}
bool AndroidSdkPackage::operator <(const AndroidSdkPackage &other) const
{
if (typeid(*this) != typeid(other))
return type() < other.type();
return displayText() < other.displayText();
}
QString AndroidSdkPackage::displayText() const
{
return m_displayText;
}
QString AndroidSdkPackage::descriptionText() const
{
return m_descriptionText;
}
const QVersionNumber &AndroidSdkPackage::revision() const
{
return m_revision;
}
AndroidSdkPackage::PackageState AndroidSdkPackage::state() const
{
return m_state;
}
const QString &AndroidSdkPackage::sdkStylePath() const
{
return m_sdkStylePath;
}
const Utils::FileName &AndroidSdkPackage::installedLocation() const
{
return m_installedLocation;
}
void AndroidSdkPackage::setDisplayText(const QString &str)
{
m_displayText = str;
}
void AndroidSdkPackage::setDescriptionText(const QString &str)
{
m_descriptionText = str;
}
void AndroidSdkPackage::setState(AndroidSdkPackage::PackageState state)
{
m_state = state;
}
void AndroidSdkPackage::setInstalledLocation(const Utils::FileName &path)
{
m_installedLocation = path;
if (m_installedLocation.exists())
updatePackageDetails();
}
void AndroidSdkPackage::updatePackageDetails()
{
}
SystemImage::SystemImage(QVersionNumber version, QString sdkStylePathStr, QString abi,
SdkPlatform *platform):
AndroidSdkPackage(version, sdkStylePathStr, platform),
m_platform(platform),
m_abiName(abi)
{
}
bool SystemImage::isValid() const
{
return m_platform && m_platform->isValid();
}
AndroidSdkPackage::PackageType SystemImage::type() const
{
return SystemImagePackage;
}
const QString &SystemImage::abiName() const
{
return m_abiName;
}
const SdkPlatform *SystemImage::platform() const
{
return m_platform.data();
}
void SystemImage::setPlatform(SdkPlatform *platform)
{
m_platform = platform;
}
SdkPlatform::SdkPlatform(QVersionNumber version, QString sdkStylePathStr, int api, QObject *parent) :
AndroidSdkPackage(version, sdkStylePathStr, parent),
m_apiLevel(api)
{
setDisplayText(QString("android-%1")
.arg(m_apiLevel != -1 ? QString::number(m_apiLevel) : "Unknown"));
}
SdkPlatform::~SdkPlatform()
{
for (SystemImage *image : m_systemImages)
delete image;
m_systemImages.clear();
}
bool SdkPlatform::isValid() const
{
return m_apiLevel != -1;
}
AndroidSdkPackage::PackageType SdkPlatform::type() const
{
return SdkPlatformPackage;
}
bool SdkPlatform::operator <(const AndroidSdkPackage &other) const
{
if (typeid(*this) != typeid(other))
return AndroidSdkPackage::operator <(other);
const SdkPlatform &platform = static_cast<const SdkPlatform &>(other);
if (platform.m_apiLevel == m_apiLevel)
return AndroidSdkPackage::operator <(other);
return platform.m_apiLevel < m_apiLevel;
}
int SdkPlatform::apiLevel() const
{
return m_apiLevel;
}
QVersionNumber SdkPlatform::version() const
{
return m_version;
}
void SdkPlatform::addSystemImage(SystemImage *image)
{
// Ordered insert. Installed images on top with lexical comparison of the display name.
auto itr = m_systemImages.begin();
while (itr != m_systemImages.end()) {
SystemImage *currentImage = *itr;
if (currentImage->state() == image->state()) {
if (currentImage->displayText() > image->displayText())
break;
} else if (currentImage->state() > image->state()) {
break;
}
++itr;
}
m_systemImages.insert(itr, image);
image->setPlatform(this);
}
SystemImageList SdkPlatform::systemImages(PackageState state) const
{
return Utils::filtered(m_systemImages, [state](const SystemImage *image) {
return image->state() & state;
});
}
BuildTools::BuildTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent):
AndroidSdkPackage(revision, sdkStylePathStr, parent)
{
}
bool BuildTools::isValid() const
{
return true;
}
AndroidSdkPackage::PackageType BuildTools::type() const
{
return AndroidSdkPackage::BuildToolsPackage;
}
SdkTools::SdkTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent):
AndroidSdkPackage(revision, sdkStylePathStr, parent)
{
}
bool SdkTools::isValid() const
{
return true;
}
AndroidSdkPackage::PackageType SdkTools::type() const
{
return AndroidSdkPackage::SdkToolsPackage;
}
PlatformTools::PlatformTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent):
AndroidSdkPackage(revision, sdkStylePathStr, parent)
{
}
bool PlatformTools::isValid() const
{
return true;
}
AndroidSdkPackage::PackageType PlatformTools::type() const
{
return AndroidSdkPackage::PlatformToolsPackage;
}
} // namespace Android

View File

@@ -0,0 +1,184 @@
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** 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 The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "utils/fileutils.h"
#include <QList>
#include <QObject>
#include <QPointer>
#include <QVersionNumber>
#pragma once
namespace Android {
namespace Internal {
class SdkManagerOutputParser;
class AndroidToolOutputParser;
}
class SdkPlatform;
class SystemImage;
class AndroidSdkPackage : public QObject
{
Q_OBJECT
public:
enum PackageType {
UnknownPackage = 1 << 0,
SdkToolsPackage = 1 << 1,
BuildToolsPackage = 1 << 2,
PlatformToolsPackage = 1 << 3,
SdkPlatformPackage = 1 << 4,
SystemImagePackage = 1 << 5,
AnyValidType = SdkToolsPackage | BuildToolsPackage | PlatformToolsPackage |
SdkPlatformPackage | SystemImagePackage
};
enum PackageState {
Unknown = 1 << 0,
Installed = 1 << 1,
Available = 1 << 2,
AnyValidState = Installed | Available
};
AndroidSdkPackage(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr);
virtual ~AndroidSdkPackage() { }
virtual bool isValid() const = 0;
virtual PackageType type() const = 0;
virtual bool operator <(const AndroidSdkPackage &other) const;
QString displayText() const;
QString descriptionText() const;
const QVersionNumber &revision() const;
PackageState state() const;
const QString &sdkStylePath() const;
const Utils::FileName &installedLocation() const;
protected:
void setDisplayText(const QString &str);
void setDescriptionText(const QString &str);
void setState(PackageState state);
void setInstalledLocation(const Utils::FileName &path);
virtual void updatePackageDetails();
private:
QString m_displayText;
QString m_descriptionText;
QVersionNumber m_revision;
PackageState m_state = PackageState::Unknown;
QString m_sdkStylePath;
Utils::FileName m_installedLocation;
friend class Internal::SdkManagerOutputParser;
friend class Internal::AndroidToolOutputParser;
};
using AndroidSdkPackageList = QList<AndroidSdkPackage*>;
class SystemImage : public AndroidSdkPackage
{
Q_OBJECT
public:
SystemImage(QVersionNumber revision, QString sdkStylePathStr, QString abi,
SdkPlatform *platform = nullptr);
// AndroidSdkPackage Overrides
bool isValid() const override;
PackageType type() const override;
const QString &abiName() const;
const SdkPlatform *platform() const;
void setPlatform(SdkPlatform *platform);
private:
QPointer<SdkPlatform> m_platform;
QString m_abiName;
};
using SystemImageList = QList<SystemImage*>;
class SdkPlatform : public AndroidSdkPackage
{
Q_OBJECT
public:
SdkPlatform(QVersionNumber revision, QString sdkStylePathStr, int api,
QObject *parent = nullptr);
~SdkPlatform();
// AndroidSdkPackage Overrides
bool isValid() const override;
PackageType type() const override;
bool operator <(const AndroidSdkPackage &other) const override;
int apiLevel() const;
QVersionNumber version() const;
void addSystemImage(SystemImage *image);
SystemImageList systemImages(AndroidSdkPackage::PackageState state
= AndroidSdkPackage::Installed) const;
private:
SystemImageList m_systemImages;
int m_apiLevel = -1;
QVersionNumber m_version;
};
using SdkPlatformList = QList<SdkPlatform*>;
class BuildTools : public AndroidSdkPackage
{
public:
BuildTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr);
// AndroidSdkPackage Overrides
public:
bool isValid() const override;
PackageType type() const override;
};
class PlatformTools : public AndroidSdkPackage
{
public:
PlatformTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr);
// AndroidSdkPackage Overrides
public:
bool isValid() const override;
PackageType type() const override;
};
class SdkTools : public AndroidSdkPackage
{
public:
SdkTools(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr);
// AndroidSdkPackage Overrides
public:
bool isValid() const override;
PackageType type() const override;
};
} // namespace Android

View File

@@ -31,8 +31,13 @@
#include "androidconstants.h"
#include "androidtoolchain.h"
#include "androidavdmanager.h"
#include "androidsdkmanager.h"
#include "avddialog.h"
#include "androidsdkmanagerwidget.h"
#include <utils/qtcassert.h>
#include <utils/environment.h>
#include <utils/elidinglabel.h>
#include <utils/hostosinfo.h>
#include <utils/pathchooser.h>
#include <utils/runextensions.h>
@@ -59,6 +64,100 @@
namespace Android {
namespace Internal {
namespace {
enum JavaValidation {
JavaPathExistsRow,
JavaJdkValidRow
};
enum AndroidValidation {
SdkPathExistsRow,
SdkToolsInstalledRow,
PlatformToolsInstalledRow,
BuildToolsInstalledRow,
PlatformSdkInstalledRow,
NdkPathExistsRow,
NdkDirStructureRow,
NdkinstallDirOkRow
};
}
class SummaryWidget : public QWidget
{
class RowData {
public:
QLabel *m_iconLabel = nullptr;
Utils::ElidingLabel *m_textLabel = nullptr;
bool m_valid = false;
};
public:
SummaryWidget(const QMap<int, QString> &validationPoints, const QString &validText,
const QString &invalidText, Utils::DetailsWidget *detailsWidget) :
QWidget(detailsWidget),
m_validText(validText),
m_invalidText(invalidText),
m_detailsWidget(detailsWidget)
{
QTC_CHECK(m_detailsWidget);
auto layout = new QGridLayout(this);
layout->setMargin(12);
int row = 0;
for (auto itr = validationPoints.cbegin(); itr != validationPoints.cend(); ++itr) {
RowData data;
data.m_iconLabel = new QLabel(this);
layout->addWidget(data.m_iconLabel, row, 0, 1, 1);
data.m_textLabel = new Utils::ElidingLabel(itr.value(), this);
data.m_textLabel->setElideMode(Qt::ElideRight);
data.m_textLabel->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
layout->addWidget(data.m_textLabel, row, 1, 1, 1);
m_validationData[itr.key()] = data;
setPointValid(itr.key(), true);
++row;
}
}
void setPointValid(int key, bool valid)
{
if (!m_validationData.contains(key))
return;
RowData& data = m_validationData[key];
data.m_valid = valid;
data.m_iconLabel->setPixmap(data.m_valid ? Utils::Icons::OK.pixmap() :
Utils::Icons::BROKEN.pixmap());
updateUi();
}
bool rowsOk(QList<int> keys) const
{
for (auto key : keys) {
if (!m_validationData[key].m_valid)
return false;
}
return true;
}
bool allRowsOk() const { return rowsOk(m_validationData.keys()); }
void setInfoText(const QString &text) {
m_infoText = text;
updateUi();
}
private:
void updateUi() {
bool ok = allRowsOk();
m_detailsWidget->setIcon(ok ? Utils::Icons::OK.icon() :
Utils::Icons::CRITICAL.icon());
m_detailsWidget->setSummaryText(ok ? QString("%1 %2").arg(m_validText).arg(m_infoText)
: m_invalidText);
}
QString m_validText;
QString m_invalidText;
QString m_infoText;
Utils::DetailsWidget *m_detailsWidget = nullptr;
QMap<int, RowData> m_validationData;
};
void AvdModel::setAvdList(const AndroidDeviceInfoList &list)
{
beginResetModel();
@@ -125,14 +224,54 @@ int AvdModel::columnCount(const QModelIndex &/*parent*/) const
AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
: QWidget(parent),
m_sdkState(NotSet),
m_ndkState(NotSet),
m_javaState(NotSet),
m_ui(new Ui_AndroidSettingsWidget),
m_androidConfig(AndroidConfigurations::currentConfig()),
m_avdManager(new AndroidAvdManager(m_androidConfig))
m_avdManager(new AndroidAvdManager(m_androidConfig)),
m_sdkManager(new AndroidSdkManager(m_androidConfig))
{
m_ui->setupUi(this);
m_sdkManagerWidget = new AndroidSdkManagerWidget(m_androidConfig, m_sdkManager.get(),
m_ui->sdkManagerTab);
auto sdkMangerLayout = new QVBoxLayout(m_ui->sdkManagerTab);
sdkMangerLayout->setMargin(0);
sdkMangerLayout->addWidget(m_sdkManagerWidget);
connect(m_sdkManagerWidget, &AndroidSdkManagerWidget::updatingSdk, [this]() {
m_ui->SDKLocationPathChooser->setEnabled(false);
// Disable the tab bar to restrict the user moving away from sdk manager tab untill
// operations finish.
m_ui->managerTabWidget->tabBar()->setEnabled(false);
});
connect(m_sdkManagerWidget, &AndroidSdkManagerWidget::updatingSdkFinished, [this]() {
m_ui->SDKLocationPathChooser->setEnabled(true);
m_ui->managerTabWidget->tabBar()->setEnabled(true);
});
QMap<int, QString> javaValidationPoints;
javaValidationPoints[JavaPathExistsRow] = tr("JDK path exists.");
javaValidationPoints[JavaJdkValidRow] = tr("JDK path is a valid JDK root folder.");
auto javaSummary = new SummaryWidget(javaValidationPoints, tr("Java Settings are OK."),
tr("Java settings have errors."), m_ui->javaDetailsWidget);
m_ui->javaDetailsWidget->setWidget(javaSummary);
QMap<int, QString> androidValidationPoints;
androidValidationPoints[SdkPathExistsRow] = tr("Android SDK path exists.");
androidValidationPoints[SdkToolsInstalledRow] = tr("SDK tools installed.");
androidValidationPoints[PlatformToolsInstalledRow] = tr("Platform tools installed.");
androidValidationPoints[BuildToolsInstalledRow] = tr("Build tools installed.");
androidValidationPoints[PlatformSdkInstalledRow] = tr("Platform SDK installed.");
androidValidationPoints[NdkPathExistsRow] = tr("Android NDK path exists.");
androidValidationPoints[NdkDirStructureRow] = tr("Android NDK directory structure is correct.");
androidValidationPoints[NdkinstallDirOkRow] = tr("Android NDK installed into a path without "
"spaces.");
auto androidSummary = new SummaryWidget(androidValidationPoints, tr("Android settings are OK."),
tr("Android settings have errors."),
m_ui->androidDetailsWidget);
m_ui->androidDetailsWidget->setWidget(androidSummary);
auto kitsDetailsLabel = new QLabel(m_ui->kitWarningDetails);
kitsDetailsLabel->setWordWrap(true);
m_ui->kitWarningDetails->setWidget(kitsDetailsLabel);
m_ui->kitWarningDetails->setIcon(Utils::Icons::WARNING.icon());
m_ui->SDKLocationPathChooser->setFileName(m_androidConfig.sdkLocation());
m_ui->SDKLocationPathChooser->setPromptDialogTitle(tr("Select Android SDK folder"));
@@ -149,29 +288,16 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
m_ui->downloadOpenJDKToolButton->setVisible(!Utils::HostOsInfo::isLinuxHost());
const QPixmap warningPixmap = Utils::Icons::WARNING.pixmap();
m_ui->jdkWarningIconLabel->setPixmap(warningPixmap);
m_ui->kitWarningIconLabel->setPixmap(warningPixmap);
const QPixmap errorPixmap = Utils::Icons::CRITICAL.pixmap();
m_ui->sdkWarningIconLabel->setPixmap(errorPixmap);
m_ui->ndkWarningIconLabel->setPixmap(errorPixmap);
connect(&m_virtualDevicesWatcher, &QFutureWatcherBase::finished,
this, &AndroidSettingsWidget::updateAvds);
check(All);
applyToUi(All);
connect(&m_futureWatcher, &QFutureWatcherBase::finished,
this, &AndroidSettingsWidget::avdAdded);
connect(m_ui->NDKLocationPathChooser, &Utils::PathChooser::rawPathChanged,
this, &AndroidSettingsWidget::ndkLocationEditingFinished);
this, &AndroidSettingsWidget::validateNdk);
connect(m_ui->SDKLocationPathChooser, &Utils::PathChooser::rawPathChanged,
this, &AndroidSettingsWidget::sdkLocationEditingFinished);
this, &AndroidSettingsWidget::onSdkPathChanged);
connect(m_ui->OpenJDKLocationPathChooser, &Utils::PathChooser::rawPathChanged,
this, &AndroidSettingsWidget::openJDKLocationEditingFinished);
this, &AndroidSettingsWidget::validateJdk);
connect(m_ui->AVDAddPushButton, &QAbstractButton::clicked,
this, &AndroidSettingsWidget::addAVD);
connect(m_ui->AVDRemovePushButton, &QAbstractButton::clicked,
@@ -184,7 +310,7 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
this, &AndroidSettingsWidget::avdActivated);
connect(m_ui->DataPartitionSizeSpinBox, &QAbstractSpinBox::editingFinished,
this, &AndroidSettingsWidget::dataPartitionSizeEditingFinished);
connect(m_ui->manageAVDPushButton, &QAbstractButton::clicked,
connect(m_ui->nativeAvdManagerButton, &QAbstractButton::clicked,
this, &AndroidSettingsWidget::manageAVD);
connect(m_ui->CreateKitCheckBox, &QAbstractButton::toggled,
this, &AndroidSettingsWidget::createKitToggled);
@@ -194,152 +320,24 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
this, &AndroidSettingsWidget::openNDKDownloadUrl);
connect(m_ui->downloadOpenJDKToolButton, &QAbstractButton::clicked,
this, &AndroidSettingsWidget::openOpenJDKDownloadUrl);
// Validate SDK again after any change in SDK packages.
connect(m_sdkManager.get(), &AndroidSdkManager::packageReloadFinished,
this, &AndroidSettingsWidget::validateSdk);
validateJdk();
validateNdk();
// Reloading SDK packages is still synchronous. Use zero timer to let settings dialog open
// first.
QTimer::singleShot(0, std::bind(&AndroidSdkManager::reloadPackages, m_sdkManager.get(), false));
}
AndroidSettingsWidget::~AndroidSettingsWidget()
{
// Deleting m_sdkManagerWidget will cancel all ongoing and pending sdkmanager operations.
delete m_sdkManagerWidget;
delete m_ui;
m_futureWatcher.waitForFinished();
}
void AndroidSettingsWidget::check(AndroidSettingsWidget::Mode mode)
{
if (mode & Sdk) {
m_sdkState = verifySdkInstallation(&m_sdkInstallationError) ? Okay : Error;
}
if (mode & Ndk) {
m_ndkState = Okay;
Utils::FileName platformPath = m_androidConfig.ndkLocation();
Utils::FileName toolChainPath = m_androidConfig.ndkLocation();
Utils::FileName sourcesPath = m_androidConfig.ndkLocation();
if (m_androidConfig.ndkLocation().isEmpty()) {
m_ndkState = NotSet;
} else if (!platformPath.appendPath(QLatin1String("platforms")).exists()
|| !toolChainPath.appendPath(QLatin1String("toolchains")).exists()
|| !sourcesPath.appendPath(QLatin1String("sources/cxx-stl")).exists()) {
m_ndkState = Error;
m_ndkErrorMessage = tr("\"%1\" does not seem to be an Android NDK top folder.")
.arg(m_androidConfig.ndkLocation().toUserOutput());
} else if (platformPath.toString().contains(QLatin1Char(' '))) {
m_ndkState = Error;
m_ndkErrorMessage = tr("The Android NDK cannot be installed into a path with spaces.");
} else {
QList<AndroidToolChainFactory::AndroidToolChainInformation> compilerPaths
= AndroidToolChainFactory::toolchainPathsForNdk(m_androidConfig.ndkLocation());
m_ndkCompilerCount = compilerPaths.count();
// See if we have qt versions for those toolchains
QSet<ProjectExplorer::Abi> toolchainsForAbi;
foreach (const AndroidToolChainFactory::AndroidToolChainInformation &ati, compilerPaths) {
if (ati.language == Core::Id(ProjectExplorer::Constants::CXX_LANGUAGE_ID))
toolchainsForAbi.insert(ati.abi);
}
const QList<QtSupport::BaseQtVersion *> androidQts
= QtSupport::QtVersionManager::versions([](const QtSupport::BaseQtVersion *v) {
return v->type() == QLatin1String(Constants::ANDROIDQT) && !v->qtAbis().isEmpty();
});
QSet<ProjectExplorer::Abi> qtVersionsForAbi;
foreach (QtSupport::BaseQtVersion *qtVersion, androidQts)
qtVersionsForAbi.insert(qtVersion->qtAbis().first());
QSet<ProjectExplorer::Abi> missingQtArchs = toolchainsForAbi.subtract(qtVersionsForAbi);
if (missingQtArchs.isEmpty()) {
m_ndkMissingQtArchs.clear();
} else {
if (missingQtArchs.count() == 1) {
m_ndkMissingQtArchs = tr("Qt version for architecture %1 is missing.\n"
"To add the Qt version, select Options > Build & Run > Qt Versions.")
.arg((*missingQtArchs.constBegin()).toString());
} else {
m_ndkMissingQtArchs = tr("Qt versions for %n architectures are missing.\n"
"To add the Qt versions, select Options > Build & Run > Qt Versions.",
nullptr, missingQtArchs.size());
}
}
}
}
if (mode & Java) {
m_javaState = Okay;
if (m_androidConfig.openJDKLocation().isEmpty()) {
m_javaState = NotSet;
} else {
Utils::FileName bin = m_androidConfig.openJDKLocation();
bin.appendPath(QLatin1String("bin/javac" QTC_HOST_EXE_SUFFIX));
if (!m_androidConfig.openJDKLocation().exists() || !bin.exists())
m_javaState = Error;
}
}
}
void AndroidSettingsWidget::applyToUi(AndroidSettingsWidget::Mode mode)
{
if (mode & Sdk) {
if (m_sdkState == Error) {
m_ui->sdkWarningIconLabel->setVisible(true);
m_ui->sdkWarningLabel->setVisible(true);
m_ui->sdkWarningLabel->setText(m_sdkInstallationError);
} else {
m_ui->sdkWarningIconLabel->setVisible(false);
m_ui->sdkWarningLabel->setVisible(false);
}
}
if (mode & Ndk) {
if (m_ndkState == NotSet) {
m_ui->ndkWarningIconLabel->setVisible(false);
m_ui->toolchainFoundLabel->setVisible(false);
m_ui->kitWarningIconLabel->setVisible(false);
m_ui->kitWarningLabel->setVisible(false);
} else if (m_ndkState == Error) {
m_ui->toolchainFoundLabel->setText(m_ndkErrorMessage);
m_ui->toolchainFoundLabel->setVisible(true);
m_ui->ndkWarningIconLabel->setVisible(true);
m_ui->kitWarningIconLabel->setVisible(false);
m_ui->kitWarningLabel->setVisible(false);
} else {
if (m_ndkCompilerCount > 0) {
m_ui->ndkWarningIconLabel->setVisible(false);
m_ui->toolchainFoundLabel->setText(tr("Found %n toolchains for this NDK.", 0, m_ndkCompilerCount));
m_ui->toolchainFoundLabel->setVisible(true);
} else {
m_ui->ndkWarningIconLabel->setVisible(false);
m_ui->toolchainFoundLabel->setVisible(false);
}
if (m_ndkMissingQtArchs.isEmpty()) {
m_ui->kitWarningIconLabel->setVisible(false);
m_ui->kitWarningLabel->setVisible(false);
} else {
m_ui->kitWarningIconLabel->setVisible(true);
m_ui->kitWarningLabel->setVisible(true);
m_ui->kitWarningLabel->setText(m_ndkMissingQtArchs);
}
}
}
if (mode & Java) {
Utils::FileName location = m_androidConfig.openJDKLocation();
bool error = m_javaState == Error;
m_ui->jdkWarningIconLabel->setVisible(error);
m_ui->jdkWarningLabel->setVisible(error);
if (error)
m_ui->jdkWarningLabel->setText(tr("\"%1\" does not seem to be a JDK folder.").arg(location.toUserOutput()));
}
if (mode & Sdk || mode & Java) {
if (m_sdkState == Okay && m_javaState == Okay) {
m_ui->AVDManagerFrame->setEnabled(true);
} else {
m_ui->AVDManagerFrame->setEnabled(false);
}
startUpdateAvd();
}
}
void AndroidSettingsWidget::disableAvdControls()
{
m_ui->AVDAddPushButton->setEnabled(false);
@@ -371,75 +369,89 @@ void AndroidSettingsWidget::updateAvds()
enableAvdControls();
}
bool AndroidSettingsWidget::verifySdkInstallation(QString *errorDetails) const
{
if (m_androidConfig.sdkLocation().isEmpty()) {
if (errorDetails)
*errorDetails = tr("Android SDK path not set.");
return false;
}
if (!m_androidConfig.sdkLocation().exists()) {
if (errorDetails)
*errorDetails = tr("Android SDK path does not exist.");
return false;
}
if (m_androidConfig.sdkToolsVersion().isNull()) {
if (errorDetails)
*errorDetails = tr("The SDK path does not seem to be a valid Android SDK top folder.");
return false;
}
QStringList missingComponents;
if (!m_androidConfig.adbToolPath().exists())
missingComponents << "Platform Tools";
if (m_androidConfig.buildToolsVersion().isNull())
missingComponents << "Build Tools";
if (m_androidConfig.sdkTargets().isEmpty())
missingComponents << "Platform SDK";
if (!missingComponents.isEmpty() && errorDetails) {
*errorDetails = tr("Android SDK components missing (%1).\nUse Android SDK Manager to "
"manage SDK components.").arg(missingComponents.join(", "));
}
return missingComponents.isEmpty();
}
void AndroidSettingsWidget::saveSettings()
{
sdkLocationEditingFinished();
ndkLocationEditingFinished();
openJDKLocationEditingFinished();
dataPartitionSizeEditingFinished();
AndroidConfigurations::setConfig(m_androidConfig);
}
void AndroidSettingsWidget::sdkLocationEditingFinished()
void AndroidSettingsWidget::validateJdk()
{
m_androidConfig.setSdkLocation(Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath()));
auto javaPath = Utils::FileName::fromUserInput(m_ui->OpenJDKLocationPathChooser->rawPath());
m_androidConfig.setOpenJDKLocation(javaPath);
bool jdkPathExists = m_androidConfig.openJDKLocation().exists();
auto summaryWidget = static_cast<SummaryWidget *>(m_ui->javaDetailsWidget->widget());
summaryWidget->setPointValid(JavaPathExistsRow, jdkPathExists);
check(Sdk);
applyToUi(Sdk);
Utils::FileName bin = m_androidConfig.openJDKLocation();
bin.appendPath(QLatin1String("bin/javac" QTC_HOST_EXE_SUFFIX));
summaryWidget->setPointValid(JavaJdkValidRow, jdkPathExists && bin.exists());
updateUI();
}
void AndroidSettingsWidget::ndkLocationEditingFinished()
void AndroidSettingsWidget::validateNdk()
{
m_androidConfig.setNdkLocation(Utils::FileName::fromUserInput(m_ui->NDKLocationPathChooser->rawPath()));
auto ndkPath = Utils::FileName::fromUserInput(m_ui->NDKLocationPathChooser->rawPath());
m_androidConfig.setNdkLocation(ndkPath);
check(Ndk);
applyToUi(Ndk);
auto summaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
summaryWidget->setPointValid(NdkPathExistsRow, m_androidConfig.ndkLocation().exists());
Utils::FileName ndkPlatformsDir(ndkPath);
ndkPlatformsDir.appendPath("platforms");
Utils::FileName ndkToolChainsDir(ndkPath);
ndkToolChainsDir.appendPath("toolchains");
Utils::FileName ndkSourcesDir(ndkPath);
ndkSourcesDir.appendPath("sources/cxx-stl");
summaryWidget->setPointValid(NdkDirStructureRow,
ndkPlatformsDir.exists()
&& ndkToolChainsDir.exists()
&& ndkSourcesDir.exists());
summaryWidget->setPointValid(NdkinstallDirOkRow,
ndkPlatformsDir.exists() &&
!ndkPlatformsDir.toString().contains(' '));
updateUI();
}
void AndroidSettingsWidget::openJDKLocationEditingFinished()
void AndroidSettingsWidget::onSdkPathChanged()
{
m_androidConfig.setOpenJDKLocation(Utils::FileName::fromUserInput(m_ui->OpenJDKLocationPathChooser->rawPath()));
auto sdkPath = Utils::FileName::fromUserInput(m_ui->SDKLocationPathChooser->rawPath());
m_androidConfig.setSdkLocation(sdkPath);
// Package reload will trigger validateSdk.
m_sdkManager->reloadPackages();
}
check(Java);
applyToUi(Java);
void AndroidSettingsWidget::validateSdk()
{
auto summaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
summaryWidget->setPointValid(SdkPathExistsRow, m_androidConfig.sdkLocation().exists());
summaryWidget->setPointValid(SdkToolsInstalledRow,
!m_androidConfig.sdkToolsVersion().isNull());
summaryWidget->setPointValid(PlatformToolsInstalledRow,
m_androidConfig.adbToolPath().exists());
summaryWidget->setPointValid(BuildToolsInstalledRow,
!m_androidConfig.buildToolsVersion().isNull());
// installedSdkPlatforms should not trigger a package reload as validate SDK is only called
// after AndroidSdkManager::packageReloadFinished.
summaryWidget->setPointValid(PlatformSdkInstalledRow,
!m_sdkManager->installedSdkPlatforms().isEmpty());
updateUI();
bool sdkToolsOk = summaryWidget->rowsOk({SdkPathExistsRow, SdkToolsInstalledRow});
bool componentsOk = summaryWidget->rowsOk({PlatformToolsInstalledRow,
BuildToolsInstalledRow,
PlatformSdkInstalledRow});
if (sdkToolsOk && !componentsOk && !m_androidConfig.useNativeUiTools()) {
// Ask user to install essential SDK components. Works only for sdk tools version >= 26.0.0
QString message = tr("Android SDK installation is missing necessary packages. Do you "
"want to install the missing packages?");
auto userInput = QMessageBox::information(this, tr("Missing Android SDK packages"),
message, QMessageBox::Yes | QMessageBox::No);
if (userInput == QMessageBox::Yes) {
m_ui->managerTabWidget->setCurrentWidget(m_ui->sdkManagerTab);
m_sdkManagerWidget->installEssentials();
}
}
}
void AndroidSettingsWidget::openSDKDownloadUrl()
@@ -460,9 +472,9 @@ void AndroidSettingsWidget::openOpenJDKDownloadUrl()
void AndroidSettingsWidget::addAVD()
{
disableAvdControls();
AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this);
CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, m_sdkManager.get());
if (!info.target.isValid()) {
if (!info.isValid()) {
enableAvdControls();
return;
}
@@ -472,7 +484,7 @@ void AndroidSettingsWidget::addAVD()
void AndroidSettingsWidget::avdAdded()
{
AndroidConfig::CreateAvdInfo info = m_futureWatcher.result();
CreateAvdInfo info = m_futureWatcher.result();
if (!info.error.isEmpty()) {
enableAvdControls();
QMessageBox::critical(this, QApplication::translate("AndroidConfig", "Error Creating AVD"), info.error);
@@ -520,9 +532,76 @@ void AndroidSettingsWidget::createKitToggled()
m_androidConfig.setAutomaticKitCreation(m_ui->CreateKitCheckBox->isChecked());
}
void AndroidSettingsWidget::checkMissingQtVersion()
{
auto summaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
if (!summaryWidget->allRowsOk()) {
m_ui->kitWarningDetails->setVisible(false);
m_ui->kitWarningDetails->setState(Utils::DetailsWidget::Collapsed);
return;
}
QList<AndroidToolChainFactory::AndroidToolChainInformation> compilerPaths
= AndroidToolChainFactory::toolchainPathsForNdk(m_androidConfig.ndkLocation());
// See if we have qt versions for those toolchains
QSet<ProjectExplorer::Abi> toolchainsForAbi;
foreach (const AndroidToolChainFactory::AndroidToolChainInformation &ati, compilerPaths) {
if (ati.language == Core::Id(ProjectExplorer::Constants::CXX_LANGUAGE_ID))
toolchainsForAbi.insert(ati.abi);
}
const QList<QtSupport::BaseQtVersion *> androidQts
= QtSupport::QtVersionManager::versions([](const QtSupport::BaseQtVersion *v) {
return v->type() == QLatin1String(Constants::ANDROIDQT) && !v->qtAbis().isEmpty();
});
QSet<ProjectExplorer::Abi> qtVersionsForAbi;
foreach (QtSupport::BaseQtVersion *qtVersion, androidQts)
qtVersionsForAbi.insert(qtVersion->qtAbis().first());
QSet<ProjectExplorer::Abi> missingQtArchs = toolchainsForAbi.subtract(qtVersionsForAbi);
bool isArchMissing = !missingQtArchs.isEmpty();
m_ui->kitWarningDetails->setVisible(isArchMissing);
if (isArchMissing) {
m_ui->kitWarningDetails->setSummaryText(tr("Cannot create kits for all architectures."));
QLabel *detailsLabel = static_cast<QLabel *>(m_ui->kitWarningDetails->widget());
QStringList archNames;
for (auto abi : missingQtArchs)
archNames << abi.toString();
detailsLabel->setText(tr("Qt versions are missing for the following architectures:\n%1"
"\n\nTo add the Qt version, select Options > Build & Run > Qt"
" Versions.").arg(archNames.join(", ")));
}
}
void AndroidSettingsWidget::updateUI()
{
auto javaSummaryWidget = static_cast<SummaryWidget *>(m_ui->javaDetailsWidget->widget());
auto androidSummaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
bool javaSetupOk = javaSummaryWidget->allRowsOk();
bool sdkToolsOk = androidSummaryWidget->rowsOk({SdkPathExistsRow, SdkToolsInstalledRow});
bool androidSetupOk = androidSummaryWidget->allRowsOk();
m_ui->avdManagerTab->setEnabled(javaSetupOk && androidSetupOk);
m_ui->sdkManagerTab->setEnabled(sdkToolsOk);
m_sdkManagerWidget->setSdkManagerControlsEnabled(!m_androidConfig.useNativeUiTools());
auto infoText = tr("(SDK Version: %1, NDK Version: %2)")
.arg(m_androidConfig.sdkToolsVersion().toString())
.arg(m_androidConfig.ndkVersion().toString());
androidSummaryWidget->setInfoText(androidSetupOk ? infoText : "");
m_ui->javaDetailsWidget->setState(javaSetupOk ? Utils::DetailsWidget::Collapsed :
Utils::DetailsWidget::Expanded);
m_ui->androidDetailsWidget->setState(androidSetupOk ? Utils::DetailsWidget::Collapsed :
Utils::DetailsWidget::Expanded);
startUpdateAvd();
checkMissingQtVersion();
}
void AndroidSettingsWidget::manageAVD()
{
if (m_avdManager->avdManagerUiToolAvailable()) {
if (m_androidConfig.useNativeUiTools()) {
m_avdManager->launchAvdManagerUiTool();
} else {
QMessageBox::warning(this, tr("AVD Manager Not Available"),

View File

@@ -42,6 +42,8 @@ QT_END_NAMESPACE
namespace Android {
namespace Internal {
class AndroidSdkManagerWidget;
class AndroidAvdManager;
class AvdModel: public QAbstractTableModel
@@ -73,9 +75,10 @@ public:
void saveSettings();
private:
void sdkLocationEditingFinished();
void ndkLocationEditingFinished();
void openJDKLocationEditingFinished();
void validateJdk();
void validateNdk();
void onSdkPathChanged();
void validateSdk();
void openSDKDownloadUrl();
void openNDKDownloadUrl();
void openOpenJDKDownloadUrl();
@@ -88,34 +91,25 @@ private:
void manageAVD();
void createKitToggled();
void checkMissingQtVersion();
void updateUI();
void updateAvds();
private:
enum Mode { Sdk = 1, Ndk = 2, Java = 4, All = Sdk | Ndk | Java };
enum State { NotSet = 0, Okay = 1, Error = 2 };
bool verifySdkInstallation(QString *errorDetails = nullptr) const;
void check(Mode mode);
void applyToUi(Mode mode);
void startUpdateAvd();
void enableAvdControls();
void disableAvdControls();
State m_sdkState;
QString m_sdkInstallationError;
State m_ndkState;
QString m_ndkErrorMessage;
int m_ndkCompilerCount;
QString m_ndkMissingQtArchs;
State m_javaState;
Ui_AndroidSettingsWidget *m_ui;
AndroidSdkManagerWidget *m_sdkManagerWidget = nullptr;
AndroidConfig m_androidConfig;
AvdModel m_AVDModel;
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcher;
QFutureWatcher<CreateAvdInfo> m_futureWatcher;
QFutureWatcher<AndroidDeviceInfoList> m_virtualDevicesWatcher;
QString m_lastAddedAvd;
std::unique_ptr<AndroidAvdManager> m_avdManager;
std::unique_ptr<AndroidSdkManager> m_sdkManager;
};
} // namespace Internal

View File

@@ -6,382 +6,348 @@
<rect>
<x>0</x>
<y>0</y>
<width>843</width>
<height>625</height>
<width>1131</width>
<height>826</height>
</rect>
</property>
<property name="windowTitle">
<string>Android Configuration</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="9" column="0" colspan="2">
<widget class="QFrame" name="AVDManagerFrame">
<property name="frameShape">
<enum>QFrame::StyledPanel</enum>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="leftMargin">
<number>4</number>
</property>
<property name="topMargin">
<number>4</number>
</property>
<property name="rightMargin">
<number>4</number>
</property>
<property name="bottomMargin">
<number>4</number>
</property>
<item>
<widget class="QGroupBox" name="javaSettingsGroup">
<property name="minimumSize">
<size>
<width>0</width>
<height>0</height>
</size>
</property>
<property name="frameShadow">
<enum>QFrame::Raised</enum>
<property name="title">
<string>Java Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="1">
<widget class="QPushButton" name="AVDStartPushButton">
<property name="enabled">
<bool>false</bool>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="1">
<widget class="Utils::PathChooser" name="OpenJDKLocationPathChooser" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="downloadOpenJDKToolButton">
<property name="toolTip">
<string>Download JDK</string>
</property>
<property name="icon">
<iconset resource="android.qrc">
<normaloff>:/android/images/download.png</normaloff>:/android/images/download.png</iconset>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="OpenJDKLocationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start...</string>
<string>JDK location:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0" colspan="2">
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QLabel" name="AVDManagerLabel">
<property name="text">
<string>AVD Manager</string>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QLabel" name="DataPartitionSizeLable">
<property name="text">
<string>System/data partition size:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="DataPartitionSizeSpinBox">
<property name="suffix">
<string> Mb</string>
</property>
<property name="maximum">
<number>99999</number>
</property>
<property name="value">
<number>1024</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="manageAVDPushButton">
<property name="text">
<string>Start AVD Manager...</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="AVDRemovePushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="AVDAddPushButton">
<property name="text">
<string>Add...</string>
</property>
</widget>
</item>
<item row="1" column="0" rowspan="4">
<widget class="QTableView" name="AVDTableView">
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideMiddle</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item row="4" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<item row="1" column="0" colspan="3">
<widget class="Utils::DetailsWidget" name="javaDetailsWidget" native="true">
<property name="minimumSize">
<size>
<width>20</width>
<height>129</height>
<width>0</width>
<height>0</height>
</size>
</property>
</spacer>
</widget>
</item>
</layout>
</widget>
</item>
<item row="0" column="1">
<widget class="Utils::PathChooser" name="OpenJDKLocationPathChooser" native="true"/>
</item>
<item row="2" column="1">
<widget class="Utils::PathChooser" name="SDKLocationPathChooser" native="true"/>
</item>
<item row="4" column="1">
<widget class="Utils::PathChooser" name="NDKLocationPathChooser" native="true"/>
</item>
<item row="1" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
<widget class="QLabel" name="jdkWarningIconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="jdkWarningLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="2">
<widget class="QToolButton" name="downloadNDKToolButton">
<property name="toolTip">
<string>Download Android NDK</string>
</property>
<property name="icon">
<iconset resource="android.qrc">
<normaloff>:/android/images/download.png</normaloff>:/android/images/download.png</iconset>
<item>
<widget class="QGroupBox" name="groupBox">
<property name="title">
<string>Android Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_3">
<item row="1" column="1">
<widget class="Utils::PathChooser" name="NDKLocationPathChooser" native="true"/>
</item>
<item row="0" column="1">
<widget class="Utils::PathChooser" name="SDKLocationPathChooser" native="true">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QToolButton" name="downloadNDKToolButton">
<property name="toolTip">
<string>Download Android NDK</string>
</property>
<property name="icon">
<iconset resource="android.qrc">
<normaloff>:/android/images/download.png</normaloff>:/android/images/download.png</iconset>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="NDKLocationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Android NDK location:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="SDKLocationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Android SDK location:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="downloadSDKToolButton">
<property name="toolTip">
<string>Download Android SDK</string>
</property>
<property name="icon">
<iconset resource="android.qrc">
<normaloff>:/android/images/download.png</normaloff>:/android/images/download.png</iconset>
</property>
</widget>
</item>
<item row="2" column="0" colspan="3">
<widget class="Utils::DetailsWidget" name="androidDetailsWidget" native="true"/>
</item>
</layout>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="SDKLocationLabel">
<item>
<widget class="QCheckBox" name="CreateKitCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Android SDK location:</string>
<string>Automatically create kits for Android tool chains</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QToolButton" name="downloadOpenJDKToolButton">
<property name="toolTip">
<string>Download JDK</string>
</property>
<property name="icon">
<iconset resource="android.qrc">
<normaloff>:/android/images/download.png</normaloff>:/android/images/download.png</iconset>
</property>
</widget>
<item>
<widget class="Utils::DetailsWidget" name="kitWarningDetails" native="true"/>
</item>
<item row="2" column="2">
<widget class="QToolButton" name="downloadSDKToolButton">
<property name="toolTip">
<string>Download Android SDK</string>
</property>
<property name="icon">
<iconset resource="android.qrc">
<normaloff>:/android/images/download.png</normaloff>:/android/images/download.png</iconset>
</property>
</widget>
</item>
<item row="8" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_2">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="kitWarningIconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="kitWarningLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="7" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_8">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<item>
<widget class="QTabWidget" name="managerTabWidget">
<property name="currentIndex">
<number>0</number>
</property>
<item>
<widget class="QCheckBox" name="CreateKitCheckBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<widget class="QWidget" name="avdManagerTab">
<attribute name="title">
<string>AVD Manager</string>
</attribute>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="leftMargin">
<number>0</number>
</property>
<property name="text">
<string>Automatically create kits for Android tool chains</string>
<property name="topMargin">
<number>0</number>
</property>
<property name="checked">
<bool>true</bool>
<property name="rightMargin">
<number>0</number>
</property>
</widget>
</item>
</layout>
</item>
<item row="4" column="0">
<widget class="QLabel" name="NDKLocationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Android NDK location:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="5" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_3">
<property name="spacing">
<number>2</number>
</property>
<item>
<widget class="QLabel" name="ndkWarningIconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
<property name="bottomMargin">
<number>0</number>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="toolchainFoundLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
</layout>
</item>
<item row="3" column="1">
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QLabel" name="sdkWarningIconLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="sdkWarningLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string/>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</item>
<item row="0" column="0">
<widget class="QLabel" name="OpenJDKLocationLabel">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>JDK location:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<item>
<widget class="QTableView" name="AVDTableView">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="selectionMode">
<enum>QAbstractItemView::SingleSelection</enum>
</property>
<property name="selectionBehavior">
<enum>QAbstractItemView::SelectRows</enum>
</property>
<property name="textElideMode">
<enum>Qt::ElideMiddle</enum>
</property>
<attribute name="verticalHeaderVisible">
<bool>false</bool>
</attribute>
</widget>
</item>
<item>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QLabel" name="DataPartitionSizeLable">
<property name="text">
<string>System/data partition size:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="DataPartitionSizeSpinBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="suffix">
<string> Mb</string>
</property>
<property name="maximum">
<number>99999</number>
</property>
<property name="value">
<number>1024</number>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="AVDAddPushButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Add...</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeType">
<enum>QSizePolicy::Fixed</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>0</width>
<height>8</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="AVDRemovePushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Remove</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="AVDStartPushButton">
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Start...</string>
</property>
</widget>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="nativeAvdManagerButton">
<property name="text">
<string>Native AVD Manager...</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWidget" name="sdkManagerTab">
<attribute name="title">
<string>SDK Manager</string>
</attribute>
</widget>
</widget>
</item>
</layout>
@@ -393,6 +359,12 @@
<header location="global">utils/pathchooser.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>Utils::DetailsWidget</class>
<extends>QWidget</extends>
<header location="global">utils/detailswidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<resources>
<include location="android.qrc"/>

View File

@@ -47,7 +47,7 @@ class AndroidToolOutputParser
{
public:
void parseTargetListing(const QString &output, const FileName &sdkLocation,
SdkPlatformList *platformList);
SdkPlatformList &platformList);
QList<SdkPlatform> m_installedPlatforms;
};
@@ -104,7 +104,7 @@ SdkPlatformList AndroidToolManager::availableSdkPlatforms(bool *ok) const
QString targetListing;
if (androidToolCommand(m_config.androidToolPath(), QStringList({"list", "target"}),
androidToolEnvironment(), &targetListing)) {
m_parser->parseTargetListing(targetListing, m_config.sdkLocation(), &list);
m_parser->parseTargetListing(targetListing, m_config.sdkLocation(), list);
success = true;
} else {
qCDebug(androidToolLog) << "Android tool target listing failed";
@@ -121,8 +121,7 @@ void AndroidToolManager::launchAvdManager() const
QProcess::startDetached(m_config.androidToolPath().toString(), QStringList("avd"));
}
QFuture<AndroidConfig::CreateAvdInfo>
AndroidToolManager::createAvd(AndroidConfig::CreateAvdInfo info) const
QFuture<CreateAvdInfo> AndroidToolManager::createAvd(CreateAvdInfo info) const
{
return Utils::runAsync(&AndroidToolManager::createAvdImpl, info,
m_config.androidToolPath(), androidToolEnvironment());
@@ -159,15 +158,14 @@ Environment AndroidToolManager::androidToolEnvironment() const
return env;
}
AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::CreateAvdInfo info,
FileName androidToolPath,
Environment env)
CreateAvdInfo AndroidToolManager::createAvdImpl(CreateAvdInfo info, FileName androidToolPath,
Environment env)
{
QProcess proc;
proc.setProcessEnvironment(env.toProcessEnvironment());
QStringList arguments;
arguments << QLatin1String("create") << QLatin1String("avd")
<< QLatin1String("-t") << AndroidConfig::apiLevelNameFor(info.target)
<< QLatin1String("-t") << AndroidConfig::apiLevelNameFor(info.sdkPlatform)
<< QLatin1String("-n") << info.name
<< QLatin1String("-b") << info.abi;
if (info.sdcardSize > 0)
@@ -293,24 +291,36 @@ AndroidDeviceInfoList AndroidToolManager::androidVirtualDevices(const Utils::Fil
void AndroidToolOutputParser::parseTargetListing(const QString &output,
const Utils::FileName &sdkLocation,
SdkPlatformList *platformList)
SdkPlatformList &platformList)
{
if (!platformList)
return;
auto addSystemImage = [](const QStringList& abiList, SdkPlatform &platform) {
auto addSystemImage = [](const QStringList& abiList, SdkPlatform *platform) {
QTC_ASSERT(platform, return);
foreach (auto imageAbi, abiList) {
SystemImage image;
image.abiName = imageAbi;
image.apiLevel = platform.apiLevel;
platform.systemImages.append(image);
auto image = new SystemImage(QVersionNumber(), "", imageAbi, platform);
platform->addSystemImage(image);
}
};
SdkPlatform platform;
QStringList abiList;
foreach (const QString &l, output.split('\n')) {
const QString line = l.trimmed();
class {
public:
QStringList abiList;
QVersionNumber revision;
int apiLevel = -1;
QString description;
Utils::FileName installedLocation;
void clear() {
abiList.clear();
revision = QVersionNumber();
apiLevel = -1;
description.clear();
installedLocation.clear();
}
} platformParams;
QStringList outputLines = output.split('\n');
for (int index = 0; index < outputLines.count(); ++index) {
const QString line = outputLines.at(index).trimmed();
if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) {
int index = line.indexOf(QLatin1String("\"android-"));
if (index == -1)
@@ -319,33 +329,33 @@ void AndroidToolOutputParser::parseTargetListing(const QString &output,
const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1);
Utils::FileName platformPath = sdkLocation;
platformPath.appendPath(QString("/platforms/android-%1").arg(tmp));
platform.installedLocation = platformPath;
platform.apiLevel = AndroidManager::findApiLevel(platformPath);
platformParams.installedLocation = platformPath;
platformParams.apiLevel = AndroidManager::findApiLevel(platformPath);
} else if (line.startsWith(QLatin1String("Name:"))) {
platform.name = line.mid(6);
platformParams.description = line.mid(6);
} else if (line.startsWith(QLatin1String("Revision:"))) {
platformParams.revision = QVersionNumber::fromString(line.mid(10));
} else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
abiList = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
platformParams.abiList = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
} else if (line.startsWith(QLatin1String("ABIs"))) {
abiList = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
} else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) {
if (platform.apiLevel == -1)
platformParams.abiList = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
} else if (line.startsWith(QLatin1String("---"))
|| line.startsWith(QLatin1String("==="))
|| index == outputLines.count() - 1) {
if (platformParams.apiLevel == -1)
continue;
addSystemImage(abiList, platform);
*platformList << platform;
platform = SdkPlatform();
abiList.clear();
auto platform = new SdkPlatform(platformParams.revision,
QString("platforms;android-%1").arg(platformParams.apiLevel),
platformParams.apiLevel);
platform->setState(AndroidSdkPackage::Installed);
platform->setDescriptionText(platformParams.description);
platform->setInstalledLocation(platformParams.installedLocation);
addSystemImage(platformParams.abiList, platform);
platformList << platform;
platformParams.clear();
}
}
// The last parsed Platform.
if (platform.apiLevel != -1) {
addSystemImage(abiList, platform);
*platformList << platform;
}
Utils::sort(*platformList);
Utils::sort(platformList);
}
} // namespace Internal

View File

@@ -51,15 +51,15 @@ public:
SdkPlatformList availableSdkPlatforms(bool *ok = nullptr) const;
void launchAvdManager() const;
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
QFuture<CreateAvdInfo> createAvd(CreateAvdInfo info) const;
bool removeAvd(const QString &name) const;
QFuture<AndroidDeviceInfoList> androidVirtualDevicesFuture() const;
// Helper methods
private:
Utils::Environment androidToolEnvironment() const;
static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info,
Utils::FileName androidToolPath, Utils::Environment env);
static CreateAvdInfo createAvdImpl(CreateAvdInfo info, Utils::FileName androidToolPath,
Utils::Environment env);
static AndroidDeviceInfoList androidVirtualDevices(const Utils::FileName &androidTool,
const Utils::FileName &sdkLlocationPath,
const Utils::Environment &environment);

View File

@@ -24,11 +24,12 @@
****************************************************************************/
#include "avddialog.h"
#include "androidconfigurations.h"
#include "androidsdkmanager.h"
#include <utils/algorithm.h>
#include <utils/tooltip/tooltip.h>
#include <utils/utilsicons.h>
#include <utils/qtcassert.h>
#include <QKeyEvent>
#include <QMessageBox>
@@ -37,10 +38,14 @@
using namespace Android;
using namespace Android::Internal;
AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidConfig *config, QWidget *parent) :
QDialog(parent), m_config(config), m_minApiLevel(minApiLevel),
AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QString &targetArch,
QWidget *parent) :
QDialog(parent),
m_sdkManager(sdkManager),
m_minApiLevel(minApiLevel),
m_allowedNameChars(QLatin1String("[a-z|A-Z|0-9|._-]*"))
{
QTC_CHECK(m_sdkManager);
m_avdDialog.setupUi(this);
m_hideTipTimer.setInterval(2000);
m_hideTipTimer.setSingleShot(true);
@@ -70,12 +75,27 @@ AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidCo
bool AvdDialog::isValid() const
{
return !name().isEmpty() && target().isValid() && !abi().isEmpty();
return !name().isEmpty() && sdkPlatform() && sdkPlatform()->isValid() && !abi().isEmpty();
}
SdkPlatform AvdDialog::target() const
CreateAvdInfo AvdDialog::gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager,
int minApiLevel, QString targetArch)
{
return m_avdDialog.targetComboBox->currentData().value<SdkPlatform>();
CreateAvdInfo result;
AvdDialog d(minApiLevel, sdkManager, targetArch, parent);
if (d.exec() != QDialog::Accepted || !d.isValid())
return result;
result.sdkPlatform = d.sdkPlatform();
result.name = d.name();
result.abi = d.abi();
result.sdcardSize = d.sdcardSize();
return result;
}
const SdkPlatform* AvdDialog::sdkPlatform() const
{
return m_avdDialog.targetComboBox->currentData().value<SdkPlatform*>();
}
QString AvdDialog::name() const
@@ -96,21 +116,23 @@ int AvdDialog::sdcardSize() const
void AvdDialog::updateApiLevelComboBox()
{
SdkPlatformList filteredList;
SdkPlatformList platforms = m_config->sdkTargets(m_minApiLevel);
const SdkPlatformList platforms = m_sdkManager->filteredSdkPlatforms(m_minApiLevel);
QString selectedAbi = abi();
auto hasAbi = [selectedAbi](const SystemImage &image) {
return image.isValid() && (image.abiName == selectedAbi);
auto hasAbi = [selectedAbi](const SystemImage *image) {
return image && image->isValid() && (image->abiName() == selectedAbi);
};
filteredList = Utils::filtered(platforms, [hasAbi](const SdkPlatform &platform) {
return Utils::anyOf(platform.systemImages,hasAbi);
filteredList = Utils::filtered(platforms, [hasAbi](const SdkPlatform *platform) {
return platform && Utils::anyOf(platform->systemImages(), hasAbi);
});
m_avdDialog.targetComboBox->clear();
foreach (const SdkPlatform &platform, filteredList) {
m_avdDialog.targetComboBox->addItem(AndroidConfig::apiLevelNameFor(platform),
QVariant::fromValue<SdkPlatform>(platform));
for (SdkPlatform *platform: filteredList) {
m_avdDialog.targetComboBox->addItem(platform->displayText(),
QVariant::fromValue<SdkPlatform *>(platform));
m_avdDialog.targetComboBox->setItemData(m_avdDialog.targetComboBox->count() - 1,
platform->descriptionText(), Qt::ToolTipRole);
}
if (platforms.isEmpty()) {

View File

@@ -24,7 +24,7 @@
****************************************************************************/
#pragma once
#include "androidconfigurations.h"
#include "ui_addnewavddialog.h"
#include <QDialog>
@@ -35,26 +35,28 @@ class AndroidConfig;
class SdkPlatform;
namespace Internal {
class AndroidSdkManager;
class AvdDialog : public QDialog
{
Q_OBJECT
public:
explicit AvdDialog(int minApiLevel, const QString &targetArch,
const AndroidConfig *config, QWidget *parent = 0);
explicit AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QString &targetArch,
QWidget *parent = 0);
Android::SdkPlatform target() const;
const SdkPlatform *sdkPlatform() const;
QString name() const;
QString abi() const;
int sdcardSize() const;
bool isValid() const;
static CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager,
int minApiLevel = 0, QString targetArch = QString());
private:
void updateApiLevelComboBox();
bool eventFilter(QObject *obj, QEvent *event);
Ui::AddNewAVDDialog m_avdDialog;
const AndroidConfig *m_config;
AndroidSdkManager *m_sdkManager;
int m_minApiLevel;
QTimer m_hideTipTimer;
QRegExp m_allowedNameChars;