diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index 67cc03f6ca4..6cd59c4b705 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -50,7 +50,8 @@ HEADERS += \ androidsdkmanager.h \ androidavdmanager.h \ androidrunconfigurationwidget.h \ - adbcommandswidget.h + adbcommandswidget.h \ + androidsdkpackage.h SOURCES += \ androidconfigurations.cpp \ @@ -94,7 +95,8 @@ SOURCES += \ androidsdkmanager.cpp \ androidavdmanager.cpp \ androidrunconfigurationwidget.cpp \ - adbcommandswidget.cpp + adbcommandswidget.cpp \ + androidsdkpackage.cpp FORMS += \ androidsettingswidget.ui \ diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 028fe2c7974..f4cf488616f 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -101,37 +101,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) @@ -234,8 +234,7 @@ void AndroidAvdManager::launchAvdManagerUiTool() const } } -QFuture -AndroidAvdManager::createAvd(AndroidConfig::CreateAvdInfo info) const +QFuture AndroidAvdManager::createAvd(CreateAvdInfo info) const { if (m_config.sdkToolsVersion() < avdManagerIntroVersion) return m_androidTool->createAvd(info); diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h index 4e8633efda9..5e079444e3d 100644 --- a/src/plugins/android/androidavdmanager.h +++ b/src/plugins/android/androidavdmanager.h @@ -42,7 +42,7 @@ public: bool avdManagerUiToolAvailable() const; void launchAvdManagerUiTool() const; - QFuture createAvd(AndroidConfig::CreateAvdInfo info) const; + QFuture createAvd(CreateAvdInfo info) const; bool removeAvd(const QString &name) const; QFuture avdList() const; diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index 8ed1316a253..7972e959ab0 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -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")); @@ -236,8 +238,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(); return ProjectExplorer::BuildStep::fromMap(map); } diff --git a/src/plugins/android/androidbuildapkwidget.cpp b/src/plugins/android/androidbuildapkwidget.cpp index 73c1ad531c3..6b386d9a7bd 100644 --- a/src/plugins/android/androidbuildapkwidget.cpp +++ b/src/plugins/android/androidbuildapkwidget.cpp @@ -29,6 +29,7 @@ #include "androidconfigurations.h" #include "androidcreatekeystorecertificate.h" #include "androidmanager.h" +#include "androidsdkmanager.h" #include "ui_androidbuildapkwidget.h" #include @@ -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()))); diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index b52810e93f0..12a2e9e991d 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -272,7 +272,6 @@ void AndroidConfig::load(const QSettings &settings) m_makeExtraSearchDirectories << extraDirectory; // persistent settings } - m_availableSdkPlatformsUpToDate = false; m_NdkInformationUpToDate = false; } @@ -333,40 +332,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 &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 AndroidConfig::sdkTargets(int minApiLevel) const -{ - updateAvailableSdkPlatforms(); - QList 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 +497,6 @@ QVector 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 devices = connectedDevices(); @@ -716,14 +676,6 @@ QStringList AndroidConfig::getAbis(const QString &adbToolPath, const QString &de return result; } -SdkPlatform AndroidConfig::highestAndroidSdk() const -{ - updateAvailableSdkPlatforms(); - if (m_availableSdkPlatforms.isEmpty()) - return SdkPlatform(); - return m_availableSdkPlatforms.first(); -} - QString AndroidConfig::bestNdkPlatformMatch(int target) const { target = std::max(9, target); @@ -743,7 +695,6 @@ FileName AndroidConfig::sdkLocation() const void AndroidConfig::setSdkLocation(const FileName &sdkLocation) { m_sdkLocation = sdkLocation; - m_availableSdkPlatformsUpToDate = false; } QVersionNumber AndroidConfig::sdkToolsVersion() const @@ -836,7 +787,6 @@ FileName AndroidConfig::openJDKLocation() const void AndroidConfig::setOpenJDKLocation(const FileName &openJDKLocation) { m_openJDKLocation = openJDKLocation; - m_availableSdkPlatformsUpToDate = false; } FileName AndroidConfig::keystoreLocation() const @@ -1129,6 +1079,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 +1098,8 @@ void AndroidConfigurations::save() } AndroidConfigurations::AndroidConfigurations(QObject *parent) - : QObject(parent) + : QObject(parent), + m_sdkManager(new AndroidSdkManager(m_config)) { load(); @@ -1155,6 +1111,11 @@ AndroidConfigurations::AndroidConfigurations(QObject *parent) m_instance = this; } +AndroidConfigurations::~AndroidConfigurations() +{ + +} + static FileName javaHomeForJavac(const FileName &location) { QFileInfo fileInfo = location.toFileInfo(); @@ -1265,13 +1226,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 diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index 75b6dd16abc..9fc349d267c 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -26,7 +26,7 @@ #pragma once #include "android_global.h" - +#include "androidsdkpackage.h" #include #include @@ -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; -//! 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; - - -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; class ANDROID_EXPORT AndroidConfig { @@ -106,9 +93,8 @@ public: void load(const QSettings &settings); void save(QSettings &settings) const; - static QStringList apiLevelNamesFor(const QList &platforms); - static QString apiLevelNameFor(const SdkPlatform &platform); - QList 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); @@ -147,19 +133,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 connectedDevices(QString *error = 0) const; static QVector connectedDevices(const QString &adbToolPath, QString *error = 0); @@ -175,7 +148,6 @@ public: OpenGl getOpenGLEnabled(const QString &emulator) const; bool isConnected(const QString &serialNumber) const; - SdkPlatform highestAndroidSdk() const; private: static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property); @@ -189,7 +161,6 @@ private: bool isBootToQt(const QString &device) const; static QString getAvdName(const QString &serialnumber); - void updateAvailableSdkPlatforms() const; void updateNdkInformation() const; Utils::FileName m_sdkLocation; @@ -201,9 +172,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 m_availableNdkPlatforms; @@ -218,6 +186,7 @@ class ANDROID_EXPORT AndroidConfigurations : public QObject public: static const AndroidConfig ¤tConfig(); + static Internal::AndroidSdkManager *sdkManager(); static void setConfig(const AndroidConfig &config); static AndroidConfigurations *instance(); @@ -236,16 +205,17 @@ signals: private: AndroidConfigurations(QObject *parent); + ~AndroidConfigurations(); void load(); void save(); static AndroidConfigurations *m_instance; AndroidConfig m_config; + std::unique_ptr m_sdkManager; QMap > m_defaultDeviceForAbi; bool m_force32bit; }; } // namespace Android -Q_DECLARE_METATYPE(Android::SdkPlatform) diff --git a/src/plugins/android/androiddevicedialog.cpp b/src/plugins/android/androiddevicedialog.cpp index 67cd02e9f84..9c07238d2dc 100644 --- a/src/plugins/android/androiddevicedialog.cpp +++ b/src/plugins/android/androiddevicedialog.cpp @@ -26,6 +26,7 @@ #include "androiddevicedialog.h" #include "androidmanager.h" #include "androidavdmanager.h" +#include "avddialog.h" #include "ui_androiddevicedialog.h" #include @@ -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; diff --git a/src/plugins/android/androiddevicedialog.h b/src/plugins/android/androiddevicedialog.h index f882b0bfcd2..db379c068e9 100644 --- a/src/plugins/android/androiddevicedialog.h +++ b/src/plugins/android/androiddevicedialog.h @@ -79,7 +79,7 @@ private: QString m_defaultDevice; std::unique_ptr m_avdManager; QVector m_connectedDevices; - QFutureWatcher m_futureWatcherAddDevice; + QFutureWatcher m_futureWatcherAddDevice; QFutureWatcher m_futureWatcherRefreshDevices; }; diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index 3691d959004..e6fe53bdc37 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -35,6 +35,7 @@ #include "androidqtversion.h" #include "androidbuildapkstep.h" #include "androidavdmanager.h" +#include "androidsdkmanager.h" #include #include @@ -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; } diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index ac518bdd21c..7bb7a79215e 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -48,13 +48,26 @@ namespace Internal { const QVersionNumber sdkManagerIntroVersion(25, 3 ,0); const char installLocationKey[] = "Installed Location:"; -const char apiLevelPropertyKey[] = "AndroidVersion.ApiLevel"; -const char abiPropertyKey[] = "SystemImage.Abi"; +const char revisionKey[] = "Version:"; +const char descriptionKey[] = "Description:"; const int sdkManagerCmdTimeoutS = 60; using namespace Utils; +int platformNameToApiLevel(const QString &platformName) +{ + int apiLevel = -1; + QRegularExpression re("(android-)(?[0-9]{1,})", + QRegularExpression::CaseInsensitiveOption); + QRegularExpressionMatch match = re.match(platformName); + if (match.hasMatch()) { + QString apiLevelStr = match.captured("apiLevel"); + apiLevel = apiLevelStr.toInt(); + } + return apiLevel; +} + /*! Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns \c true if \a key is found, false otherwise. Result is copied into \a value. @@ -75,7 +88,7 @@ static bool valueForKey(QString key, const QString &line, QString *value = nullp \c true if the command is successfully executed. Output is copied into \a output. The function blocks the calling thread. */ -static bool sdkManagerCommand(const AndroidConfig config, const QStringList &args, QString *output, +static bool sdkManagerCommand(const AndroidConfig &config, const QStringList &args, QString *output, int timeout = sdkManagerCmdTimeoutS) { QString sdkManagerToolPath = config.sdkManagerToolPath().toString(); @@ -88,6 +101,29 @@ static bool sdkManagerCommand(const AndroidConfig config, const QStringList &arg return response.result == SynchronousProcessResponse::Finished; } + +class AndroidSdkManagerPrivate +{ +public: + AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager, const AndroidConfig &config); + ~AndroidSdkManagerPrivate(); + + AndroidSdkPackageList filteredPackages(AndroidSdkPackage::PackageState state, + AndroidSdkPackage::PackageType type, + bool forceUpdate = false); + const AndroidSdkPackageList &allPackages(bool forceUpdate = false); + void refreshSdkPackages(bool forceReload = false); + +private: + void reloadSdkPackages(); + void clearPackages(); + + AndroidSdkManager &m_sdkManager; + const AndroidConfig &m_config; + AndroidSdkPackageList m_allPackages; + FileName lastSdkManagerPath; +}; + /*! \class SdkManagerOutputParser \brief The SdkManagerOutputParser class is a helper class to parse the output of the \c sdkmanager @@ -108,19 +144,20 @@ public: SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker }; + SdkManagerOutputParser(AndroidSdkPackageList &container) : m_packages(container) {} void parsePackageListing(const QString &output); - SdkPlatformList m_installedPlatforms; + AndroidSdkPackageList &m_packages; private: - void compileData(); + void compilePackageAssociations(); void parsePackageData(MarkerTag packageMarker, const QStringList &data); - bool parsePlatform(const QStringList &data, SdkPlatform *platform) const; - bool parseSystemImage(const QStringList &data, SystemImage *image); + AndroidSdkPackage *parsePlatform(const QStringList &data) const; + QPair parseSystemImage(const QStringList &data) const; MarkerTag parseMarkers(const QString &line); MarkerTag m_currentSection = MarkerTag::None; - SystemImageList m_installedSystemImages; + QHash m_systemImages; }; const std::map markerTags { @@ -131,9 +168,9 @@ const std::map markerTags { {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"} }; -AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config): - m_config(config), - m_parser(new SdkManagerOutputParser) +AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config, QObject *parent): + QObject(parent), + m_d(new AndroidSdkManagerPrivate(*this, config)) { } @@ -142,21 +179,61 @@ AndroidSdkManager::~AndroidSdkManager() } -SdkPlatformList AndroidSdkManager::availableSdkPlatforms(bool *ok) +SdkPlatformList AndroidSdkManager::installedSdkPlatforms() { - bool success = false; - if (m_config.sdkToolsVersion() < sdkManagerIntroVersion) { - AndroidToolManager toolManager(m_config); - return toolManager.availableSdkPlatforms(ok); + AndroidSdkPackageList list = m_d->filteredPackages(AndroidSdkPackage::Installed, + AndroidSdkPackage::SdkPlatformPackage); + return Utils::transform(list, [](AndroidSdkPackage *p) { + return static_cast(p); + }); +} + +const AndroidSdkPackageList &AndroidSdkManager::allSdkPackages() +{ + return m_d->allPackages(); +} + +AndroidSdkPackageList AndroidSdkManager::availableSdkPackages() +{ + return m_d->filteredPackages(AndroidSdkPackage::Available, AndroidSdkPackage::AnyValidType); +} + +AndroidSdkPackageList AndroidSdkManager::installedSdkPackages() +{ + return m_d->filteredPackages(AndroidSdkPackage::Installed, AndroidSdkPackage::AnyValidType); +} + +SdkPlatform *AndroidSdkManager::latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state) +{ + SdkPlatform *result = nullptr; + const AndroidSdkPackageList list = m_d->filteredPackages(state, + AndroidSdkPackage::SdkPlatformPackage); + for (AndroidSdkPackage *p : list) { + auto platform = static_cast(p); + if (!result || result->apiLevel() < platform->apiLevel()) + result = platform; } + return result; +} - QString packageListing; - if (sdkManagerCommand(m_config, QStringList({"--list", "--verbose"}), &packageListing)) - m_parser->parsePackageListing(packageListing); +SdkPlatformList AndroidSdkManager::filteredSdkPlatforms(int minApiLevel, + AndroidSdkPackage::PackageState state) +{ + const AndroidSdkPackageList list = m_d->filteredPackages(state, + AndroidSdkPackage::SdkPlatformPackage); - if (ok) - *ok = success; - return m_parser->m_installedPlatforms; + SdkPlatformList result; + for (AndroidSdkPackage *p : list) { + auto platform = static_cast(p); + if (platform && platform->apiLevel() >= minApiLevel) + result << platform; + } + return result; +} + +void AndroidSdkManager::reloadPackages(bool forceReload) +{ + m_d->refreshSdkPackages(forceReload); } void SdkManagerOutputParser::parsePackageListing(const QString &output) @@ -173,10 +250,9 @@ void SdkManagerOutputParser::parsePackageListing(const QString &output) } }; - QRegularExpression delimiters("[\n\r]"); + QRegularExpression delimiters("[\\n\\r]"); foreach (QString outputLine, output.split(delimiters)) { MarkerTag marker = parseMarkers(outputLine.trimmed()); - if (marker & SectionMarkers) { // Section marker found. Update the current section being parsed. m_currentSection = marker; @@ -211,20 +287,27 @@ void SdkManagerOutputParser::parsePackageListing(const QString &output) packageData << outputLine; } } - compileData(); - Utils::sort(m_installedPlatforms); + compilePackageAssociations(); } -void SdkManagerOutputParser::compileData() +void SdkManagerOutputParser::compilePackageAssociations() { // Associate the system images with sdk platforms. - for (auto &image : m_installedSystemImages) { - auto findPlatfom = [image](const SdkPlatform &platform) { - return platform.apiLevel == image.apiLevel; + auto imageItr = m_systemImages.cbegin(); + while (imageItr != m_systemImages.cend()) { + auto findPlatform = [imageItr](const AndroidSdkPackage *p) { + const SdkPlatform *platform = nullptr; + if (p->type() == AndroidSdkPackage::SdkPlatformPackage) + platform = static_cast(p); + return platform && platform->apiLevel() == imageItr.value(); }; - auto itr = std::find_if(m_installedPlatforms.begin(), m_installedPlatforms.end(), findPlatfom); - if (itr != m_installedPlatforms.end()) - itr->systemImages.append(image); + auto itr = std::find_if(m_packages.begin(), m_packages.end(), + findPlatform); + if (itr != m_packages.end()) { + SdkPlatform *platform = static_cast(*itr); + platform->addSystemImage(static_cast(imageItr.key())); + } + ++imageItr; } } @@ -235,24 +318,23 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt if (m_currentSection != MarkerTag::InstalledPackagesMarker) return; // For now, only interested in installed packages. + AndroidSdkPackage *package = nullptr; switch (packageMarker) { case MarkerTag::PlatformMarker: - { - SdkPlatform platform; - if (parsePlatform(data, &platform)) - m_installedPlatforms.append(platform); - else - qCDebug(sdkManagerLog) << "Platform: Parsing failed: " << data; - } + package = parsePlatform(data); + if (package) + m_packages.append(package); break; case MarkerTag::SystemImageMarker: { - SystemImage image; - if (parseSystemImage(data, &image)) - m_installedSystemImages.append(image); - else + QPair result = parseSystemImage(data); + if (result.first) { + m_systemImages[result.first] = result.second; + package = result.first; + } else { qCDebug(sdkManagerLog) << "System Image: Parsing failed: " << data; + } } break; @@ -260,71 +342,118 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt qCDebug(sdkManagerLog) << "Unhandled package: " << markerTags.at(packageMarker); break; } -} -bool SdkManagerOutputParser::parsePlatform(const QStringList &data, SdkPlatform *platform) const -{ - QTC_ASSERT(platform && !data.isEmpty(), return false); - - QStringList parts = data.at(0).split(';'); - if (parts.count() < 2) { - qCDebug(sdkManagerLog) << "Platform: Unexpected header: "<< data; - return false; - } - platform->name = parts[1]; - platform->package = data.at(0); - - foreach (QString line, data) { - QString value; - if (valueForKey(installLocationKey, line, &value)) - platform->installedLocation = Utils::FileName::fromString(value); - } - - int apiLevel = AndroidManager::findApiLevel(platform->installedLocation); - if (apiLevel != -1) - platform->apiLevel = apiLevel; - else - qCDebug(sdkManagerLog) << "Platform: Can not parse api level: "<< data; - - return apiLevel != -1; -} - -bool SdkManagerOutputParser::parseSystemImage(const QStringList &data, SystemImage *image) -{ - QTC_ASSERT(image && !data.isEmpty(), return false); - - QStringList parts = data.at(0).split(';'); - QTC_ASSERT(!data.isEmpty() && parts.count() >= 4, - qCDebug(sdkManagerLog) << "System Image: Unexpected header: " << data); - - image->package = data.at(0); - foreach (QString line, data) { - QString value; - if (valueForKey(installLocationKey, line, &value)) - image->installedLocation = Utils::FileName::fromString(value); - } - - Utils::FileName propertiesPath = image->installedLocation; - propertiesPath.appendPath("/source.properties"); - if (propertiesPath.exists()) { - // Installed System Image. - QSettings imageProperties(propertiesPath.toString(), QSettings::IniFormat); - bool validApiLevel = false; - image->apiLevel = imageProperties.value(apiLevelPropertyKey).toInt(&validApiLevel); - if (!validApiLevel) { - qCDebug(sdkManagerLog) << "System Image: Can not parse api level: "<< data; - return false; + if (package) { + switch (m_currentSection) { + case MarkerTag::InstalledPackagesMarker: + package->setState(AndroidSdkPackage::Installed); + break; + case MarkerTag::AvailablePackagesMarkers: + case MarkerTag::AvailableUpdatesMarker: + package->setState(AndroidSdkPackage::Available); + break; + default: + qCDebug(sdkManagerLog) << "Invalid section marker: " << markerTags.at(m_currentSection); + break; } - image->abiName = imageProperties.value(abiPropertyKey).toString(); - } else if (parts.count() >= 4){ - image->apiLevel = parts[1].section('-', 1, 1).toInt(); - image->abiName = parts[3]; + } +} + +AndroidSdkPackage *SdkManagerOutputParser::parsePlatform(const QStringList &data) const +{ + QTC_ASSERT(!data.isEmpty(), qCDebug(sdkManagerLog) << "Platform: Empty input"; return nullptr); + + QStringList parts = data.at(0).split(';'); + QTC_ASSERT(parts.count() >= 2, + qCDebug(sdkManagerLog) << "Platform: Unexpected header:"<< data; return nullptr); + + int apiLevel = platformNameToApiLevel(parts.at(1)); + if (apiLevel == -1) { + qCDebug(sdkManagerLog) << "Platform: Can not parse api level:"<< data; + return nullptr; + } + + QVersionNumber revision; + QString description; + Utils::FileName installedLocation; + foreach (QString line, data) { + QString value; + if (valueForKey(installLocationKey, line, &value)) { + installedLocation = Utils::FileName::fromString(value); + continue; + } + + if (valueForKey(revisionKey, line, &value)) { + revision = QVersionNumber::fromString(value); + continue; + } + + if (valueForKey(descriptionKey, line, &value)) { + description = value; + continue; + } + } + + SdkPlatform *platform = nullptr; + if (!revision.isNull() && apiLevel != -1) { + platform = new SdkPlatform(revision, data.at(0), apiLevel); + platform->setDescriptionText(description); + platform->setInstalledLocation(installedLocation); + } else { + qCDebug(sdkManagerLog) << "Platform: Parsing failed. Minimum required data unavailable:" + << data; + } + + return platform; +} + +QPair SdkManagerOutputParser::parseSystemImage(const QStringList &data) const +{ + QPair result(nullptr, -1); + QTC_ASSERT(!data.isEmpty(), + qCDebug(sdkManagerLog) << "System Image: Empty input"; return result); + + QStringList parts = data.at(0).split(';'); + QTC_ASSERT(parts.count() >= 4, + qCDebug(sdkManagerLog) << "System Image: Unexpected header:" << data; return result); + + int apiLevel = platformNameToApiLevel(parts.at(1)); + if (apiLevel == -1) { + qCDebug(sdkManagerLog) << "System Image: Can not parse api level:"<< data; + return result; + } + + QVersionNumber revision; + QString description; + Utils::FileName installedLocation; + foreach (QString line, data) { + QString value; + if (valueForKey(installLocationKey, line, &value)) { + installedLocation = Utils::FileName::fromString(value); + continue; + } + + if (valueForKey(revisionKey, line, &value)) { + revision = QVersionNumber::fromString(value); + continue; + } + + if (valueForKey(descriptionKey, line, &value)) { + description = value; + continue; + } + } + + if (!revision.isNull()) { + auto image = new SystemImage(revision, data.at(0), parts.at(3)); + image->setInstalledLocation(installedLocation); + image->setDisplayText(description); + image->setDescriptionText(description); + result = qMakePair(image, apiLevel); } else { qCDebug(sdkManagerLog) << "System Image: Can not parse: "<< data; - return false; } - - return true; + return result; } SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QString &line) @@ -340,5 +469,79 @@ SdkManagerOutputParser::MarkerTag SdkManagerOutputParser::parseMarkers(const QSt return None; } +AndroidSdkManagerPrivate::AndroidSdkManagerPrivate(AndroidSdkManager &sdkManager, + const AndroidConfig &config): + m_sdkManager(sdkManager), + m_config(config) +{ + +} + +AndroidSdkManagerPrivate::~AndroidSdkManagerPrivate() +{ + clearPackages(); +} + +AndroidSdkPackageList +AndroidSdkManagerPrivate::filteredPackages(AndroidSdkPackage::PackageState state, + AndroidSdkPackage::PackageType type, bool forceUpdate) +{ + refreshSdkPackages(forceUpdate); + return Utils::filtered(m_allPackages, [state, type](const AndroidSdkPackage *p) { + return p->state() & state && p->type() & type; + }); +} + +const AndroidSdkPackageList &AndroidSdkManagerPrivate::allPackages(bool forceUpdate) +{ + refreshSdkPackages(forceUpdate); + return m_allPackages; +} + +void AndroidSdkManagerPrivate::reloadSdkPackages() +{ + m_sdkManager.packageReloadBegin(); + clearPackages(); + + lastSdkManagerPath = m_config.sdkManagerToolPath(); + + if (m_config.sdkToolsVersion().isNull()) { + // Configuration has invalid sdk path or corrupt installation. + m_sdkManager.packageReloadFinished(); + return; + } + + if (m_config.sdkToolsVersion() < sdkManagerIntroVersion) { + // Old Sdk tools. + AndroidToolManager toolManager(m_config); + auto toAndroidSdkPackages = [](SdkPlatform *p) -> AndroidSdkPackage *{ + return p; + }; + m_allPackages = Utils::transform(toolManager.availableSdkPlatforms(), toAndroidSdkPackages); + } else { + QString packageListing; + if (sdkManagerCommand(m_config, QStringList({"--list", "--verbose"}), &packageListing)) { + SdkManagerOutputParser parser(m_allPackages); + parser.parsePackageListing(packageListing); + } + } + m_sdkManager.packageReloadFinished(); +} + +void AndroidSdkManagerPrivate::refreshSdkPackages(bool forceReload) +{ + // Sdk path changed. Updated packages. + // QTC updates the package listing only + if (m_config.sdkManagerToolPath() != lastSdkManagerPath || forceReload) + reloadSdkPackages(); +} + +void AndroidSdkManagerPrivate::clearPackages() +{ + for (AndroidSdkPackage *p : m_allPackages) + delete p; + m_allPackages.clear(); +} + } // namespace Internal } // namespace Android diff --git a/src/plugins/android/androidsdkmanager.h b/src/plugins/android/androidsdkmanager.h index b1da078b6ef..3f2880c66fe 100644 --- a/src/plugins/android/androidsdkmanager.h +++ b/src/plugins/android/androidsdkmanager.h @@ -25,26 +25,46 @@ #pragma once #include "utils/fileutils.h" -#include "androidconfigurations.h" +#include "androidsdkpackage.h" + +#include #include namespace Android { + +class AndroidConfig; + namespace Internal { -class SdkManagerOutputParser; +class AndroidSdkManagerPrivate; -class AndroidSdkManager +class AndroidSdkManager : public QObject { + Q_OBJECT public: - AndroidSdkManager(const AndroidConfig &config); + 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); + +signals: + void packageReloadBegin(); + void packageReloadFinished(); private: - const AndroidConfig &m_config; - std::unique_ptr m_parser; + std::unique_ptr m_d; + friend class AndroidSdkManagerPrivate; }; } // namespace Internal diff --git a/src/plugins/android/androidsdkpackage.cpp b/src/plugins/android/androidsdkpackage.cpp new file mode 100644 index 00000000000..0ea964af97d --- /dev/null +++ b/src/plugins/android/androidsdkpackage.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** 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" + +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(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) +{ + m_systemImages.append(image); + image->setPlatform(this); +} + +const SystemImageList &SdkPlatform::systemImages() const +{ + return m_systemImages; +} + +} // namespace Android diff --git a/src/plugins/android/androidsdkpackage.h b/src/plugins/android/androidsdkpackage.h new file mode 100644 index 00000000000..2ffbd3b4a87 --- /dev/null +++ b/src/plugins/android/androidsdkpackage.h @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** 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 +#include +#include +#include + +#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; + +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 m_platform; + QString m_abiName; +}; +using SystemImageList = QList; + + +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); + const SystemImageList &systemImages() const; + +private: + SystemImageList m_systemImages; + int m_apiLevel = -1; + QVersionNumber m_version; +}; +using SdkPlatformList = QList; +} // namespace Android + + diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index 72c896038f6..4a7149847e7 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -31,6 +31,8 @@ #include "androidconstants.h" #include "androidtoolchain.h" #include "androidavdmanager.h" +#include "androidsdkmanager.h" +#include "avddialog.h" #include #include @@ -212,7 +214,8 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent) : QWidget(parent), 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); @@ -390,7 +393,7 @@ void AndroidSettingsWidget::validateSdk() summaryWidget->setPointValid(BuildToolsInstalledRow, !m_androidConfig.buildToolsVersion().isNull()); summaryWidget->setPointValid(PlatformSdkInstalledRow, - !m_androidConfig.sdkTargets().isEmpty()); + !m_sdkManager->installedSdkPlatforms().isEmpty()); updateUI(); } @@ -412,9 +415,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; } @@ -424,7 +427,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); diff --git a/src/plugins/android/androidsettingswidget.h b/src/plugins/android/androidsettingswidget.h index 41c6a32650b..5d81ad8333f 100644 --- a/src/plugins/android/androidsettingswidget.h +++ b/src/plugins/android/androidsettingswidget.h @@ -100,11 +100,12 @@ private: Ui_AndroidSettingsWidget *m_ui; AndroidConfig m_androidConfig; AvdModel m_AVDModel; - QFutureWatcher m_futureWatcher; + QFutureWatcher m_futureWatcher; QFutureWatcher m_virtualDevicesWatcher; QString m_lastAddedAvd; std::unique_ptr m_avdManager; + std::unique_ptr m_sdkManager; }; } // namespace Internal diff --git a/src/plugins/android/androidtoolmanager.cpp b/src/plugins/android/androidtoolmanager.cpp index 39fa2a2c426..fc214ab1139 100644 --- a/src/plugins/android/androidtoolmanager.cpp +++ b/src/plugins/android/androidtoolmanager.cpp @@ -47,7 +47,7 @@ class AndroidToolOutputParser { public: void parseTargetListing(const QString &output, const FileName &sdkLocation, - SdkPlatformList *platformList); + SdkPlatformList &platformList); QList 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 -AndroidToolManager::createAvd(AndroidConfig::CreateAvdInfo info) const +QFuture 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 diff --git a/src/plugins/android/androidtoolmanager.h b/src/plugins/android/androidtoolmanager.h index faaa9428ab6..61f7018bf92 100644 --- a/src/plugins/android/androidtoolmanager.h +++ b/src/plugins/android/androidtoolmanager.h @@ -51,15 +51,15 @@ public: SdkPlatformList availableSdkPlatforms(bool *ok = nullptr) const; void launchAvdManager() const; - QFuture createAvd(AndroidConfig::CreateAvdInfo info) const; + QFuture createAvd(CreateAvdInfo info) const; bool removeAvd(const QString &name) const; QFuture 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); diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index 03d29d4e232..702b2120d79 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -24,11 +24,12 @@ ****************************************************************************/ #include "avddialog.h" -#include "androidconfigurations.h" +#include "androidsdkmanager.h" #include #include #include +#include #include #include @@ -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(); + 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(); } 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(platform)); + for (SdkPlatform *platform: filteredList) { + m_avdDialog.targetComboBox->addItem(platform->displayText(), + QVariant::fromValue(platform)); + m_avdDialog.targetComboBox->setItemData(m_avdDialog.targetComboBox->count() - 1, + platform->descriptionText(), Qt::ToolTipRole); } if (platforms.isEmpty()) { diff --git a/src/plugins/android/avddialog.h b/src/plugins/android/avddialog.h index d22e6af64f5..8a30b613c6b 100644 --- a/src/plugins/android/avddialog.h +++ b/src/plugins/android/avddialog.h @@ -24,7 +24,7 @@ ****************************************************************************/ #pragma once - +#include "androidconfigurations.h" #include "ui_addnewavddialog.h" #include @@ -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;