forked from qt-creator/qt-creator
Android: Automatically download SDK tools and essential packages
Automatically download Android SDK Tools to default path used by Android Studio, then essential packages will be installed using the sdkmanager tool. Automatic installation can also be triggered by an added button in the settings page. Essentials packages include NDK Bundle and other NDK versions required by previous Qt versions. An sdk_definitions.json file holds download paths for SDK Tools, and other (Qt version <-> essential packages) combinations. [ChangeLog][Android] Automatically download SDK Tools, NDKs and all essential packages for Android builds. Task-number: QTCREATORBUG-23285 Change-Id: I90e7aafecd017d2bdc959e403711d9d440a6bbb2 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
This commit is contained in:
@@ -1,4 +1,4 @@
|
|||||||
set(template_directories cplusplus debugger glsl modeleditor qml qmldesigner
|
set(template_directories android cplusplus debugger glsl modeleditor qml qmldesigner
|
||||||
qmlicons qml-type-descriptions schemes scripts snippets styles templates themes welcomescreen)
|
qmlicons qml-type-descriptions schemes scripts snippets styles templates themes welcomescreen)
|
||||||
|
|
||||||
add_custom_target(copy_share_to_builddir ALL
|
add_custom_target(copy_share_to_builddir ALL
|
||||||
|
25
share/qtcreator/android/sdk_definitions.json
Normal file
25
share/qtcreator/android/sdk_definitions.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"common": {
|
||||||
|
"sdk_tools_url": {
|
||||||
|
"linux": "https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip",
|
||||||
|
"linux_sha256": "92ffee5a1d98d856634e8b71132e8a95d96c83a63fde1099be3d86df3106def9",
|
||||||
|
"windows": "https://dl.google.com/android/repository/sdk-tools-windows-4333796.zip",
|
||||||
|
"windows_sha256": "7e81d69c303e47a4f0e748a6352d85cd0c8fd90a5a95ae4e076b5e5f960d3c7a",
|
||||||
|
"mac": "https://dl.google.com/android/repository/sdk-tools-darwin-4333796.zip",
|
||||||
|
"mac_sha256": "ecb29358bc0f13d7c2fa0f9290135a5b608e38434aad9bf7067d0252c160853e"
|
||||||
|
},
|
||||||
|
"sdk_essential_packages": ["platform-tools", "platforms;android-29"]
|
||||||
|
},
|
||||||
|
"specific_qt_versions": [
|
||||||
|
{
|
||||||
|
"versions": ["default"],
|
||||||
|
"sdk_essential_packages": ["build-tools;29.0.2", "ndk-bundle"],
|
||||||
|
"ndk_path": "ndk-bundle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"versions": ["5.12.[0-5]", "5.13.[0-1]"],
|
||||||
|
"sdk_essential_packages": ["build-tools;28.0.2", "ndk;19.2.5345600"],
|
||||||
|
"ndk_path": "ndk/19.2.5345600"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
@@ -23,7 +23,8 @@ DATA_DIRS = \
|
|||||||
modeleditor \
|
modeleditor \
|
||||||
glsl \
|
glsl \
|
||||||
cplusplus \
|
cplusplus \
|
||||||
indexer_preincludes
|
indexer_preincludes \
|
||||||
|
android
|
||||||
macx: DATA_DIRS += scripts
|
macx: DATA_DIRS += scripts
|
||||||
|
|
||||||
for(data_dir, DATA_DIRS) {
|
for(data_dir, DATA_DIRS) {
|
||||||
|
@@ -33,6 +33,7 @@ add_qtc_plugin(Android
|
|||||||
androidruncontrol.cpp androidruncontrol.h
|
androidruncontrol.cpp androidruncontrol.h
|
||||||
androidrunner.cpp androidrunner.h
|
androidrunner.cpp androidrunner.h
|
||||||
androidrunnerworker.cpp androidrunnerworker.h
|
androidrunnerworker.cpp androidrunnerworker.h
|
||||||
|
androidsdkdownloader.cpp androidsdkdownloader.h
|
||||||
androidsdkmanager.cpp androidsdkmanager.h
|
androidsdkmanager.cpp androidsdkmanager.h
|
||||||
androidsdkmanagerwidget.cpp androidsdkmanagerwidget.h androidsdkmanagerwidget.ui
|
androidsdkmanagerwidget.cpp androidsdkmanagerwidget.h androidsdkmanagerwidget.ui
|
||||||
androidsdkmodel.cpp androidsdkmodel.h
|
androidsdkmodel.cpp androidsdkmodel.h
|
||||||
|
@@ -48,7 +48,8 @@ HEADERS += \
|
|||||||
androidsdkmanagerwidget.h \
|
androidsdkmanagerwidget.h \
|
||||||
androidpackageinstallationstep.h \
|
androidpackageinstallationstep.h \
|
||||||
androidextralibrarylistmodel.h \
|
androidextralibrarylistmodel.h \
|
||||||
createandroidmanifestwizard.h
|
createandroidmanifestwizard.h \
|
||||||
|
androidsdkdownloader.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
androidconfigurations.cpp \
|
androidconfigurations.cpp \
|
||||||
@@ -90,7 +91,8 @@ SOURCES += \
|
|||||||
androidsdkmanagerwidget.cpp \
|
androidsdkmanagerwidget.cpp \
|
||||||
androidpackageinstallationstep.cpp \
|
androidpackageinstallationstep.cpp \
|
||||||
androidextralibrarylistmodel.cpp \
|
androidextralibrarylistmodel.cpp \
|
||||||
createandroidmanifestwizard.cpp
|
createandroidmanifestwizard.cpp \
|
||||||
|
androidsdkdownloader.cpp
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
androidsettingswidget.ui \
|
androidsettingswidget.ui \
|
||||||
|
@@ -61,6 +61,9 @@
|
|||||||
#include <QDirIterator>
|
#include <QDirIterator>
|
||||||
#include <QFileInfo>
|
#include <QFileInfo>
|
||||||
#include <QHostAddress>
|
#include <QHostAddress>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonObject>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
@@ -72,6 +75,7 @@
|
|||||||
#include <functional>
|
#include <functional>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
|
using namespace QtSupport;
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
@@ -82,11 +86,21 @@ static Q_LOGGING_CATEGORY(avdConfigLog, "qtc.android.androidconfig", QtWarningMs
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
using namespace Internal;
|
using namespace Internal;
|
||||||
|
|
||||||
|
const char JsonFilePath[] = "/android/sdk_definitions.json";
|
||||||
|
const char SdkToolsUrlKey[] = "sdk_tools_url";
|
||||||
|
const char CommonKey[] = "common";
|
||||||
|
const char SdkEssentialPkgsKey[] = "sdk_essential_packages";
|
||||||
|
const char VersionsKey[] = "versions";
|
||||||
|
const char NdkPathKey[] = "ndk_path";
|
||||||
|
const char SpecificQtVersionsKey[] = "specific_qt_versions";
|
||||||
|
const char DefaultVersionKey[] = "default";
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
const char jdkSettingsPath[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit";
|
const char jdkSettingsPath[] = "HKEY_LOCAL_MACHINE\\SOFTWARE\\JavaSoft\\Java Development Kit";
|
||||||
|
|
||||||
const QLatin1String SettingsGroup("AndroidConfigurations");
|
const QLatin1String SettingsGroup("AndroidConfigurations");
|
||||||
const QLatin1String SDKLocationKey("SDKLocation");
|
const QLatin1String SDKLocationKey("SDKLocation");
|
||||||
|
const QLatin1String SdkFullyConfiguredKey("AllEssentialsInstalled");
|
||||||
const QLatin1String SDKManagerToolArgsKey("SDKManagerToolArgs");
|
const QLatin1String SDKManagerToolArgsKey("SDKManagerToolArgs");
|
||||||
const QLatin1String NDKLocationKey("NDKLocation");
|
const QLatin1String NDKLocationKey("NDKLocation");
|
||||||
const QLatin1String OpenJDKLocationKey("OpenJDKLocation");
|
const QLatin1String OpenJDKLocationKey("OpenJDKLocation");
|
||||||
@@ -228,6 +242,7 @@ void AndroidConfig::load(const QSettings &settings)
|
|||||||
m_keystoreLocation = FilePath::fromString(settings.value(KeystoreLocationKey).toString());
|
m_keystoreLocation = FilePath::fromString(settings.value(KeystoreLocationKey).toString());
|
||||||
m_toolchainHost = settings.value(ToolchainHostKey).toString();
|
m_toolchainHost = settings.value(ToolchainHostKey).toString();
|
||||||
m_automaticKitCreation = settings.value(AutomaticKitCreationKey, true).toBool();
|
m_automaticKitCreation = settings.value(AutomaticKitCreationKey, true).toBool();
|
||||||
|
m_sdkFullyConfigured = settings.value(SdkFullyConfiguredKey, false).toBool();
|
||||||
|
|
||||||
PersistentSettingsReader reader;
|
PersistentSettingsReader reader;
|
||||||
if (reader.load(FilePath::fromString(sdkSettingsFileName()))
|
if (reader.load(FilePath::fromString(sdkSettingsFileName()))
|
||||||
@@ -240,8 +255,10 @@ void AndroidConfig::load(const QSettings &settings)
|
|||||||
m_keystoreLocation = FilePath::fromString(reader.restoreValue(KeystoreLocationKey, m_keystoreLocation.toString()).toString());
|
m_keystoreLocation = FilePath::fromString(reader.restoreValue(KeystoreLocationKey, m_keystoreLocation.toString()).toString());
|
||||||
m_toolchainHost = reader.restoreValue(ToolchainHostKey, m_toolchainHost).toString();
|
m_toolchainHost = reader.restoreValue(ToolchainHostKey, m_toolchainHost).toString();
|
||||||
m_automaticKitCreation = reader.restoreValue(AutomaticKitCreationKey, m_automaticKitCreation).toBool();
|
m_automaticKitCreation = reader.restoreValue(AutomaticKitCreationKey, m_automaticKitCreation).toBool();
|
||||||
|
m_sdkFullyConfigured = reader.restoreValue(SdkFullyConfiguredKey, m_sdkFullyConfigured).toBool();
|
||||||
// persistent settings
|
// persistent settings
|
||||||
}
|
}
|
||||||
|
parseDependenciesJson();
|
||||||
m_NdkInformationUpToDate = false;
|
m_NdkInformationUpToDate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,6 +277,7 @@ void AndroidConfig::save(QSettings &settings) const
|
|||||||
settings.setValue(PartitionSizeKey, m_partitionSize);
|
settings.setValue(PartitionSizeKey, m_partitionSize);
|
||||||
settings.setValue(AutomaticKitCreationKey, m_automaticKitCreation);
|
settings.setValue(AutomaticKitCreationKey, m_automaticKitCreation);
|
||||||
settings.setValue(ToolchainHostKey, m_toolchainHost);
|
settings.setValue(ToolchainHostKey, m_toolchainHost);
|
||||||
|
settings.setValue(SdkFullyConfiguredKey, m_sdkFullyConfigured);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidConfig::updateNdkInformation() const
|
void AndroidConfig::updateNdkInformation() const
|
||||||
@@ -298,6 +316,72 @@ void AndroidConfig::updateNdkInformation() const
|
|||||||
m_NdkInformationUpToDate = true;
|
m_NdkInformationUpToDate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidConfig::parseDependenciesJson()
|
||||||
|
{
|
||||||
|
QString sdkConfigFile(Core::ICore::resourcePath() + JsonFilePath);
|
||||||
|
QFile jsonFile(sdkConfigFile);
|
||||||
|
if (!jsonFile.open(QIODevice::ReadOnly))
|
||||||
|
qCDebug(avdConfigLog, "Couldn't open JSON config file %s.", qPrintable(sdkConfigFile));
|
||||||
|
|
||||||
|
QJsonDocument loadDoc(QJsonDocument::fromJson(jsonFile.readAll()));
|
||||||
|
QJsonObject jsonObject = loadDoc.object();
|
||||||
|
|
||||||
|
if (jsonObject.contains(CommonKey) && jsonObject[CommonKey].isObject()) {
|
||||||
|
QJsonObject commonObject = jsonObject[CommonKey].toObject();
|
||||||
|
// Parse SDK Tools URL
|
||||||
|
if (commonObject.contains(SdkToolsUrlKey) && commonObject[SdkToolsUrlKey].isObject()) {
|
||||||
|
QJsonObject sdkToolsObj(commonObject[SdkToolsUrlKey].toObject());
|
||||||
|
if (Utils::HostOsInfo::isMacHost()) {
|
||||||
|
m_sdkToolsUrl = sdkToolsObj["mac"].toString();
|
||||||
|
m_sdkToolsSha256 = QByteArray::fromHex(sdkToolsObj["mac_sha256"].toString().toUtf8());
|
||||||
|
} else if (Utils::HostOsInfo::isWindowsHost()) {
|
||||||
|
m_sdkToolsUrl = sdkToolsObj["windows"].toString();
|
||||||
|
m_sdkToolsSha256 = QByteArray::fromHex(sdkToolsObj["windows_sha256"].toString().toUtf8());
|
||||||
|
} else {
|
||||||
|
m_sdkToolsUrl = sdkToolsObj["linux"].toString();
|
||||||
|
m_sdkToolsSha256 = QByteArray::fromHex(sdkToolsObj["linux_sha256"].toString().toUtf8());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse common essential packages
|
||||||
|
QJsonArray commonEssentials = commonObject[SdkEssentialPkgsKey].toArray();
|
||||||
|
for (const QJsonValueRef &pkg : commonEssentials)
|
||||||
|
m_commonEssentialPkgs.append(pkg.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fillQtVersionsRange = [](const QString &shortVersion) {
|
||||||
|
QList<QtVersionNumber> versions;
|
||||||
|
QRegularExpression re("([0-9]\\.[0-9]*\\.)\\[([0-9])\\-([0-9])\\]");
|
||||||
|
QRegularExpressionMatch match = re.match(shortVersion);
|
||||||
|
if (match.hasMatch() && match.lastCapturedIndex() == 3)
|
||||||
|
for (int i = match.captured(2).toInt(); i <= match.captured(3).toInt(); ++i)
|
||||||
|
versions.append(QtVersionNumber(match.captured(1) + QString::number(i)));
|
||||||
|
else
|
||||||
|
versions.append(QtVersionNumber(shortVersion));
|
||||||
|
|
||||||
|
return versions;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (jsonObject.contains(SpecificQtVersionsKey) && jsonObject[SpecificQtVersionsKey].isArray()) {
|
||||||
|
QJsonArray versionsArray = jsonObject[SpecificQtVersionsKey].toArray();
|
||||||
|
for (const QJsonValueRef &item : versionsArray) {
|
||||||
|
QJsonObject itemObj = item.toObject();
|
||||||
|
SdkForQtVersions specificVersion;
|
||||||
|
|
||||||
|
specificVersion.ndkPath = itemObj[NdkPathKey].toString();
|
||||||
|
for (const QJsonValueRef &pkg : itemObj[SdkEssentialPkgsKey].toArray())
|
||||||
|
specificVersion.essentialPackages.append(pkg.toString());
|
||||||
|
for (const QJsonValueRef &pkg : itemObj[VersionsKey].toArray())
|
||||||
|
specificVersion.versions.append(fillQtVersionsRange(pkg.toString()));
|
||||||
|
|
||||||
|
if (itemObj[VersionsKey].toArray().first().toString() == DefaultVersionKey)
|
||||||
|
m_defaultSdkDepends = specificVersion;
|
||||||
|
else
|
||||||
|
m_specificQtVersions.append(specificVersion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
QStringList AndroidConfig::apiLevelNamesFor(const SdkPlatformList &platforms)
|
QStringList AndroidConfig::apiLevelNamesFor(const SdkPlatformList &platforms)
|
||||||
{
|
{
|
||||||
return Utils::transform(platforms, AndroidConfig::apiLevelNameFor);
|
return Utils::transform(platforms, AndroidConfig::apiLevelNameFor);
|
||||||
@@ -709,6 +793,7 @@ QVersionNumber AndroidConfig::sdkToolsVersion() const
|
|||||||
|
|
||||||
QVersionNumber AndroidConfig::buildToolsVersion() const
|
QVersionNumber AndroidConfig::buildToolsVersion() const
|
||||||
{
|
{
|
||||||
|
//TODO: return version according to qt version
|
||||||
QVersionNumber maxVersion;
|
QVersionNumber maxVersion;
|
||||||
QDir buildToolsDir(m_sdkLocation.pathAppended("build-tools").toString());
|
QDir buildToolsDir(m_sdkLocation.pathAppended("build-tools").toString());
|
||||||
for (const QFileInfo &file: buildToolsDir.entryList(QDir::Dirs|QDir::NoDotAndDotDot))
|
for (const QFileInfo &file: buildToolsDir.entryList(QDir::Dirs|QDir::NoDotAndDotDot))
|
||||||
@@ -732,6 +817,11 @@ FilePath AndroidConfig::ndkLocation() const
|
|||||||
return m_ndkLocation;
|
return m_ndkLocation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
FilePath AndroidConfig::defaultNdkLocation() const
|
||||||
|
{
|
||||||
|
return sdkLocation().pathAppended(m_defaultSdkDepends.ndkPath);
|
||||||
|
}
|
||||||
|
|
||||||
static inline QString gdbServerArch(const QString &androidAbi)
|
static inline QString gdbServerArch(const QString &androidAbi)
|
||||||
{
|
{
|
||||||
if (androidAbi == "arm64-v8a") {
|
if (androidAbi == "arm64-v8a") {
|
||||||
@@ -808,6 +898,57 @@ void AndroidConfig::setNdkLocation(const FilePath &ndkLocation)
|
|||||||
m_NdkInformationUpToDate = false;
|
m_NdkInformationUpToDate = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList AndroidConfig::allEssentials() const
|
||||||
|
{
|
||||||
|
QList<BaseQtVersion *> installedVersions = QtVersionManager::versions(
|
||||||
|
[](const BaseQtVersion *v) {
|
||||||
|
return v->targetDeviceTypes().contains(Android::Constants::ANDROID_DEVICE_TYPE);
|
||||||
|
});
|
||||||
|
|
||||||
|
QStringList allPackages(defaultEssentials());
|
||||||
|
for (const BaseQtVersion *version : installedVersions)
|
||||||
|
allPackages.append(essentialsFromQtVersion(*version));
|
||||||
|
allPackages.removeDuplicates();
|
||||||
|
|
||||||
|
return allPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AndroidConfig::essentialsFromQtVersion(const BaseQtVersion &version) const
|
||||||
|
{
|
||||||
|
QtVersionNumber qtVersion = version.qtVersion();
|
||||||
|
for (const SdkForQtVersions &item : m_specificQtVersions)
|
||||||
|
if (item.containsVersion(qtVersion))
|
||||||
|
return item.essentialPackages;
|
||||||
|
|
||||||
|
return m_defaultSdkDepends.essentialPackages;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidConfig::ndkPathFromQtVersion(const BaseQtVersion &version) const
|
||||||
|
{
|
||||||
|
QtVersionNumber qtVersion = version.qtVersion();
|
||||||
|
for (const SdkForQtVersions &item : m_specificQtVersions)
|
||||||
|
if (item.containsVersion(qtVersion))
|
||||||
|
return item.ndkPath;
|
||||||
|
|
||||||
|
return m_defaultSdkDepends.ndkPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList AndroidConfig::defaultEssentials() const
|
||||||
|
{
|
||||||
|
return m_defaultSdkDepends.essentialPackages + m_commonEssentialPkgs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidConfig::updateDependenciesConfig()
|
||||||
|
{
|
||||||
|
parseDependenciesJson();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SdkForQtVersions::containsVersion(const QtVersionNumber &qtVersion) const
|
||||||
|
{
|
||||||
|
return versions.contains(qtVersion)
|
||||||
|
|| versions.contains(QtVersionNumber(qtVersion.majorVersion, qtVersion.minorVersion));
|
||||||
|
}
|
||||||
|
|
||||||
FilePath AndroidConfig::openJDKLocation() const
|
FilePath AndroidConfig::openJDKLocation() const
|
||||||
{
|
{
|
||||||
return m_openJDKLocation;
|
return m_openJDKLocation;
|
||||||
|
@@ -28,6 +28,7 @@
|
|||||||
#include "android_global.h"
|
#include "android_global.h"
|
||||||
#include "androidsdkpackage.h"
|
#include "androidsdkpackage.h"
|
||||||
#include <projectexplorer/toolchain.h>
|
#include <projectexplorer/toolchain.h>
|
||||||
|
#include <qtsupport/qtversionmanager.h>
|
||||||
|
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QProcessEnvironment>
|
#include <QProcessEnvironment>
|
||||||
@@ -95,6 +96,16 @@ public:
|
|||||||
bool overwrite = false;
|
bool overwrite = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct SdkForQtVersions
|
||||||
|
{
|
||||||
|
QList<QtSupport::QtVersionNumber> versions;
|
||||||
|
QStringList essentialPackages;
|
||||||
|
QString ndkPath;
|
||||||
|
|
||||||
|
public:
|
||||||
|
bool containsVersion(const QtSupport::QtVersionNumber &qtVersion) const;
|
||||||
|
};
|
||||||
|
|
||||||
class ANDROID_EXPORT AndroidConfig
|
class ANDROID_EXPORT AndroidConfig
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@@ -112,10 +123,20 @@ public:
|
|||||||
void setSdkManagerToolArgs(const QStringList &args);
|
void setSdkManagerToolArgs(const QStringList &args);
|
||||||
|
|
||||||
Utils::FilePath ndkLocation() const;
|
Utils::FilePath ndkLocation() const;
|
||||||
|
Utils::FilePath defaultNdkLocation() const;
|
||||||
Utils::FilePath gdbServer(const QString &androidAbi) const;
|
Utils::FilePath gdbServer(const QString &androidAbi) const;
|
||||||
QVersionNumber ndkVersion() const;
|
QVersionNumber ndkVersion() const;
|
||||||
void setNdkLocation(const Utils::FilePath &ndkLocation);
|
void setNdkLocation(const Utils::FilePath &ndkLocation);
|
||||||
|
|
||||||
|
QUrl sdkToolsUrl() const { return m_sdkToolsUrl; };
|
||||||
|
QByteArray getSdkToolsSha256() const { return m_sdkToolsSha256; };
|
||||||
|
QString ndkPathFromQtVersion(const QtSupport::BaseQtVersion &version) const;
|
||||||
|
|
||||||
|
QStringList defaultEssentials() const;
|
||||||
|
QStringList essentialsFromQtVersion(const QtSupport::BaseQtVersion &version) const;
|
||||||
|
QStringList allEssentials() const;
|
||||||
|
void updateDependenciesConfig();
|
||||||
|
|
||||||
Utils::FilePath openJDKLocation() const;
|
Utils::FilePath openJDKLocation() const;
|
||||||
void setOpenJDKLocation(const Utils::FilePath &openJDKLocation);
|
void setOpenJDKLocation(const Utils::FilePath &openJDKLocation);
|
||||||
|
|
||||||
@@ -162,6 +183,9 @@ public:
|
|||||||
|
|
||||||
bool useNativeUiTools() const;
|
bool useNativeUiTools() const;
|
||||||
|
|
||||||
|
bool sdkFullyConfigured() const { return m_sdkFullyConfigured; };
|
||||||
|
void setSdkFullyConfigured(bool allEssentialsInstalled) { m_sdkFullyConfigured = allEssentialsInstalled; };
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static QString getDeviceProperty(const Utils::FilePath &adbToolPath,
|
static QString getDeviceProperty(const Utils::FilePath &adbToolPath,
|
||||||
const QString &device, const QString &property);
|
const QString &device, const QString &property);
|
||||||
@@ -176,6 +200,7 @@ private:
|
|||||||
static QString getAvdName(const QString &serialnumber);
|
static QString getAvdName(const QString &serialnumber);
|
||||||
|
|
||||||
void updateNdkInformation() const;
|
void updateNdkInformation() const;
|
||||||
|
void parseDependenciesJson();
|
||||||
|
|
||||||
Utils::FilePath m_sdkLocation;
|
Utils::FilePath m_sdkLocation;
|
||||||
QStringList m_sdkManagerToolArgs;
|
QStringList m_sdkManagerToolArgs;
|
||||||
@@ -184,6 +209,12 @@ private:
|
|||||||
Utils::FilePath m_keystoreLocation;
|
Utils::FilePath m_keystoreLocation;
|
||||||
unsigned m_partitionSize = 1024;
|
unsigned m_partitionSize = 1024;
|
||||||
bool m_automaticKitCreation = true;
|
bool m_automaticKitCreation = true;
|
||||||
|
QUrl m_sdkToolsUrl;
|
||||||
|
QByteArray m_sdkToolsSha256;
|
||||||
|
QStringList m_commonEssentialPkgs;
|
||||||
|
SdkForQtVersions m_defaultSdkDepends;
|
||||||
|
QList<SdkForQtVersions> m_specificQtVersions;
|
||||||
|
bool m_sdkFullyConfigured = false;
|
||||||
|
|
||||||
//caches
|
//caches
|
||||||
mutable bool m_NdkInformationUpToDate = false;
|
mutable bool m_NdkInformationUpToDate = false;
|
||||||
|
@@ -46,6 +46,10 @@
|
|||||||
# include "androidqbspropertyprovider.h"
|
# include "androidqbspropertyprovider.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <coreplugin/icore.h>
|
||||||
|
#include <coreplugin/infobar.h>
|
||||||
|
#include <utils/checkablemessagebox.h>
|
||||||
|
|
||||||
#include <projectexplorer/devicesupport/devicemanager.h>
|
#include <projectexplorer/devicesupport/devicemanager.h>
|
||||||
#include <projectexplorer/buildconfiguration.h>
|
#include <projectexplorer/buildconfiguration.h>
|
||||||
#include <projectexplorer/deployconfiguration.h>
|
#include <projectexplorer/deployconfiguration.h>
|
||||||
@@ -60,6 +64,8 @@
|
|||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
using namespace ProjectExplorer::Constants;
|
using namespace ProjectExplorer::Constants;
|
||||||
|
|
||||||
|
const char kSetupAndroidSetting[] = "ConfigureAndroid";
|
||||||
|
|
||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -153,6 +159,10 @@ bool AndroidPlugin::initialize(const QStringList &arguments, QString *errorMessa
|
|||||||
|
|
||||||
d = new AndroidPluginPrivate;
|
d = new AndroidPluginPrivate;
|
||||||
|
|
||||||
|
if (!AndroidConfigurations::currentConfig().sdkFullyConfigured()) {
|
||||||
|
connect(Core::ICore::instance(), &Core::ICore::coreOpened, this,
|
||||||
|
&AndroidPlugin::askUserAboutAndroidSetup, Qt::QueuedConnection);
|
||||||
|
}
|
||||||
connect(KitManager::instance(), &KitManager::kitsLoaded,
|
connect(KitManager::instance(), &KitManager::kitsLoaded,
|
||||||
this, &AndroidPlugin::kitsRestored);
|
this, &AndroidPlugin::kitsRestored);
|
||||||
|
|
||||||
@@ -168,5 +178,25 @@ void AndroidPlugin::kitsRestored()
|
|||||||
this, &AndroidPlugin::kitsRestored);
|
this, &AndroidPlugin::kitsRestored);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidPlugin::askUserAboutAndroidSetup()
|
||||||
|
{
|
||||||
|
if (!Utils::CheckableMessageBox::shouldAskAgain(Core::ICore::settings(), kSetupAndroidSetting)
|
||||||
|
|| !Core::ICore::infoBar()->canInfoBeAdded(kSetupAndroidSetting))
|
||||||
|
return;
|
||||||
|
|
||||||
|
Core::InfoBarEntry info(
|
||||||
|
kSetupAndroidSetting,
|
||||||
|
tr("Would you like to configure Android options? This will ensure "
|
||||||
|
"Android kits can be usable and all essential packages are installed. "
|
||||||
|
"To do it later, select Options > Devices > Android."),
|
||||||
|
Core::InfoBarEntry::GlobalSuppression::Enabled);
|
||||||
|
info.setCustomButtonInfo(tr("Configure Android"), [this] {
|
||||||
|
Core::ICore::infoBar()->removeInfo(kSetupAndroidSetting);
|
||||||
|
Core::ICore::infoBar()->globallySuppressInfo(kSetupAndroidSetting);
|
||||||
|
QTimer::singleShot(0, this, [this]() { d->potentialKit.executeFromMenu(); });
|
||||||
|
});
|
||||||
|
Core::ICore::infoBar()->addInfo(info);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Internal
|
} // namespace Internal
|
||||||
} // namespace Android
|
} // namespace Android
|
||||||
|
@@ -40,6 +40,7 @@ class AndroidPlugin final : public ExtensionSystem::IPlugin
|
|||||||
bool initialize(const QStringList &arguments, QString *errorMessage) final;
|
bool initialize(const QStringList &arguments, QString *errorMessage) final;
|
||||||
|
|
||||||
void kitsRestored();
|
void kitsRestored();
|
||||||
|
void askUserAboutAndroidSetup();
|
||||||
|
|
||||||
class AndroidPluginPrivate *d = nullptr;
|
class AndroidPluginPrivate *d = nullptr;
|
||||||
};
|
};
|
||||||
|
231
src/plugins/android/androidsdkdownloader.cpp
Normal file
231
src/plugins/android/androidsdkdownloader.cpp
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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 "androidsdkdownloader.h"
|
||||||
|
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDirIterator>
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
#include <QProcess>
|
||||||
|
#include <QCryptographicHash>
|
||||||
|
#include <QStandardPaths>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Q_LOGGING_CATEGORY(sdkDownloaderLog, "qtc.android.sdkDownloader", QtWarningMsg)
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
namespace Internal {
|
||||||
|
/**
|
||||||
|
* @class SdkDownloader
|
||||||
|
* @brief Download Android SDK tools package from within Qt Creator.
|
||||||
|
*/
|
||||||
|
AndroidSdkDownloader::AndroidSdkDownloader(const QUrl &sdkUrl, const QByteArray &sha256) :
|
||||||
|
m_sdkUrl(sdkUrl), m_sha256(sha256)
|
||||||
|
{
|
||||||
|
connect(&m_manager, &QNetworkAccessManager::finished, this, &AndroidSdkDownloader::downloadFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if QT_CONFIG(ssl)
|
||||||
|
void AndroidSdkDownloader::sslErrors(const QList<QSslError> &sslErrors)
|
||||||
|
{
|
||||||
|
for (const QSslError &error : sslErrors)
|
||||||
|
qCDebug(sdkDownloaderLog, "SSL error: %s\n", qPrintable(error.errorString()));
|
||||||
|
cancelWithError(tr("Encountered SSL errors, download is aborted."));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static void setSdkFilesExecPermission( const QString &sdkExtractPath)
|
||||||
|
{
|
||||||
|
QDirIterator it(sdkExtractPath + "/tools", QStringList() << "*",
|
||||||
|
QDir::Files, QDirIterator::Subdirectories);
|
||||||
|
while (it.hasNext()) {
|
||||||
|
QFile file(it.next());
|
||||||
|
if (!file.fileName().contains('.')) {
|
||||||
|
QFlags<QFileDevice::Permission> currentPermissions
|
||||||
|
= file.permissions();
|
||||||
|
file.setPermissions(currentPermissions | QFileDevice::ExeOwner);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidSdkDownloader::downloadAndExtractSdk(const QString &jdkPath, const QString &sdkExtractPath)
|
||||||
|
{
|
||||||
|
if (m_sdkUrl.isEmpty()) {
|
||||||
|
logError(tr("The SDK Tools download URL is empty."));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QNetworkRequest request(m_sdkUrl);
|
||||||
|
m_reply = m_manager.get(request);
|
||||||
|
|
||||||
|
#if QT_CONFIG(ssl)
|
||||||
|
connect(m_reply, &QNetworkReply::sslErrors, this, &AndroidSdkDownloader::sslErrors);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
m_progressDialog = new QProgressDialog(tr("Downloading SDK Tools package..."), tr("Cancel"), 0, 100);
|
||||||
|
m_progressDialog->setWindowModality(Qt::WindowModal);
|
||||||
|
m_progressDialog->setWindowTitle(dialogTitle());
|
||||||
|
m_progressDialog->setFixedSize(m_progressDialog->sizeHint());
|
||||||
|
|
||||||
|
connect(m_reply, &QNetworkReply::downloadProgress, this, [this](qint64 received, qint64 max) {
|
||||||
|
m_progressDialog->setRange(0, max);
|
||||||
|
m_progressDialog->setValue(received);
|
||||||
|
});
|
||||||
|
|
||||||
|
connect(m_progressDialog, &QProgressDialog::canceled, this, &AndroidSdkDownloader::cancel);
|
||||||
|
|
||||||
|
connect(this, &AndroidSdkDownloader::sdkPackageWriteFinished, this, [this, jdkPath, sdkExtractPath]() {
|
||||||
|
if (extractSdk(jdkPath, sdkExtractPath)) {
|
||||||
|
setSdkFilesExecPermission(sdkExtractPath);
|
||||||
|
emit sdkExtracted();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidSdkDownloader::extractSdk(const QString &jdkPath, const QString &sdkExtractPath)
|
||||||
|
{
|
||||||
|
if (!QDir(sdkExtractPath).exists()) {
|
||||||
|
if (!QDir().mkdir(sdkExtractPath)) {
|
||||||
|
logError(QString(tr("Could not create the SDK folder %1.")).arg(sdkExtractPath));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QProcess *jarExtractProc = new QProcess();
|
||||||
|
jarExtractProc->setWorkingDirectory(sdkExtractPath);
|
||||||
|
QString jarCmdPath(jdkPath + "/bin/jar");
|
||||||
|
jarExtractProc->start(jarCmdPath, {"xf", m_sdkFilename});
|
||||||
|
jarExtractProc->waitForFinished();
|
||||||
|
jarExtractProc->close();
|
||||||
|
|
||||||
|
return jarExtractProc->exitCode() ? false : true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidSdkDownloader::verifyFileIntegrity()
|
||||||
|
{
|
||||||
|
QFile f(m_sdkFilename);
|
||||||
|
if (f.open(QFile::ReadOnly)) {
|
||||||
|
QCryptographicHash hash(QCryptographicHash::Sha256);
|
||||||
|
if (hash.addData(&f)) {
|
||||||
|
return hash.result() == m_sha256;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidSdkDownloader::dialogTitle()
|
||||||
|
{
|
||||||
|
return tr("Download SDK Tools");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidSdkDownloader::cancel()
|
||||||
|
{
|
||||||
|
if (m_reply) {
|
||||||
|
m_reply->abort();
|
||||||
|
m_reply->deleteLater();
|
||||||
|
}
|
||||||
|
if (m_progressDialog)
|
||||||
|
m_progressDialog->hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidSdkDownloader::cancelWithError(const QString &error)
|
||||||
|
{
|
||||||
|
cancel();
|
||||||
|
logError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidSdkDownloader::logError(const QString &error)
|
||||||
|
{
|
||||||
|
qCDebug(sdkDownloaderLog, "%s", error.toUtf8().data());
|
||||||
|
emit sdkDownloaderError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString AndroidSdkDownloader::getSaveFilename(const QUrl &url)
|
||||||
|
{
|
||||||
|
QString path = url.path();
|
||||||
|
QString basename = QFileInfo(path).fileName();
|
||||||
|
|
||||||
|
if (basename.isEmpty())
|
||||||
|
basename = "sdk-tools.zip";
|
||||||
|
|
||||||
|
if (QFile::exists(basename)) {
|
||||||
|
int i = 0;
|
||||||
|
basename += '.';
|
||||||
|
while (QFile::exists(basename + QString::number(i)))
|
||||||
|
++i;
|
||||||
|
basename += QString::number(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString fullPath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)
|
||||||
|
+ QDir::separator() + basename;
|
||||||
|
return fullPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidSdkDownloader::saveToDisk(const QString &filename, QIODevice *data)
|
||||||
|
{
|
||||||
|
QFile file(filename);
|
||||||
|
if (!file.open(QIODevice::WriteOnly)) {
|
||||||
|
logError(QString(tr("Could not open %1 for writing: %2.")).arg(filename, file.errorString()));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
file.write(data->readAll());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidSdkDownloader::isHttpRedirect(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
||||||
|
return statusCode == 301 || statusCode == 302 || statusCode == 303 || statusCode == 305
|
||||||
|
|| statusCode == 307 || statusCode == 308;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidSdkDownloader::downloadFinished(QNetworkReply *reply)
|
||||||
|
{
|
||||||
|
QUrl url = reply->url();
|
||||||
|
if (reply->error()) {
|
||||||
|
cancelWithError(QString(tr("Downloading Android SDK Tools from URL %1 has failed: %2."))
|
||||||
|
.arg(url.toString(), reply->errorString()));
|
||||||
|
} else {
|
||||||
|
if (isHttpRedirect(reply)) {
|
||||||
|
cancelWithError(QString(tr("Download from %1 was redirected.")).arg(url.toString()));
|
||||||
|
} else {
|
||||||
|
m_sdkFilename = getSaveFilename(url);
|
||||||
|
if (saveToDisk(m_sdkFilename, reply) && verifyFileIntegrity())
|
||||||
|
emit sdkPackageWriteFinished();
|
||||||
|
else
|
||||||
|
cancelWithError(
|
||||||
|
tr("Writing and verifying the integrity of the downloaded file has failed."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reply->deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Internal
|
||||||
|
} // Android
|
78
src/plugins/android/androidsdkdownloader.h
Normal file
78
src/plugins/android/androidsdkdownloader.h
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2020 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.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#ifndef ANDROIDSDKDOWNLOADER_H
|
||||||
|
#define ANDROIDSDKDOWNLOADER_H
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QProgressDialog>
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class AndroidSdkDownloader : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
AndroidSdkDownloader(const QUrl &sdkUrl, const QByteArray &sha256);
|
||||||
|
void downloadAndExtractSdk(const QString &jdkPath, const QString &sdkExtractPath);
|
||||||
|
static QString dialogTitle();
|
||||||
|
|
||||||
|
void cancel();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void sdkPackageWriteFinished();
|
||||||
|
void sdkExtracted();
|
||||||
|
void sdkDownloaderError(const QString &error);
|
||||||
|
|
||||||
|
private:
|
||||||
|
static QString getSaveFilename(const QUrl &url);
|
||||||
|
bool saveToDisk(const QString &filename, QIODevice *data);
|
||||||
|
static bool isHttpRedirect(QNetworkReply *m_reply);
|
||||||
|
|
||||||
|
bool extractSdk(const QString &jdkPath, const QString &sdkExtractPath);
|
||||||
|
bool verifyFileIntegrity();
|
||||||
|
void cancelWithError(const QString &error);
|
||||||
|
void logError(const QString &error);
|
||||||
|
|
||||||
|
void downloadFinished(QNetworkReply *m_reply);
|
||||||
|
#if QT_CONFIG(ssl)
|
||||||
|
void sslErrors(const QList<QSslError> &errors);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
QNetworkAccessManager m_manager;
|
||||||
|
QNetworkReply *m_reply;
|
||||||
|
QString m_sdkFilename;
|
||||||
|
QProgressDialog *m_progressDialog;
|
||||||
|
QUrl m_sdkUrl;
|
||||||
|
QByteArray m_sha256;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // Internal
|
||||||
|
} // Android
|
||||||
|
|
||||||
|
#endif // ANDROIDSDKDOWNLOADER_H
|
@@ -271,7 +271,8 @@ public:
|
|||||||
SdkToolsMarker = 0x100,
|
SdkToolsMarker = 0x100,
|
||||||
PlatformToolsMarker = 0x200,
|
PlatformToolsMarker = 0x200,
|
||||||
EmulatorToolsMarker = 0x400,
|
EmulatorToolsMarker = 0x400,
|
||||||
ExtrasMarker = 0x800,
|
NdkMarker = 0x800,
|
||||||
|
ExtrasMarker = 0x1000,
|
||||||
SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker
|
SectionMarkers = InstalledPackagesMarker | AvailablePackagesMarkers | AvailableUpdatesMarker
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -292,6 +293,7 @@ private:
|
|||||||
SdkTools *parseSdkToolsPackage(const QStringList &data) const;
|
SdkTools *parseSdkToolsPackage(const QStringList &data) const;
|
||||||
PlatformTools *parsePlatformToolsPackage(const QStringList &data) const;
|
PlatformTools *parsePlatformToolsPackage(const QStringList &data) const;
|
||||||
EmulatorTools *parseEmulatorToolsPackage(const QStringList &data) const;
|
EmulatorTools *parseEmulatorToolsPackage(const QStringList &data) const;
|
||||||
|
Ndk *parseNdkPackage(const QStringList &data) const;
|
||||||
ExtraTools *parseExtraToolsPackage(const QStringList &data) const;
|
ExtraTools *parseExtraToolsPackage(const QStringList &data) const;
|
||||||
MarkerTag parseMarkers(const QString &line);
|
MarkerTag parseMarkers(const QString &line);
|
||||||
|
|
||||||
@@ -309,6 +311,7 @@ const std::map<SdkManagerOutputParser::MarkerTag, const char *> markerTags {
|
|||||||
{SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"},
|
{SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"},
|
||||||
{SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"},
|
{SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"},
|
||||||
{SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"},
|
{SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"},
|
||||||
|
{SdkManagerOutputParser::MarkerTag::NdkMarker, "ndk"},
|
||||||
{SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"}
|
{SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -360,6 +363,13 @@ SystemImageList AndroidSdkManager::installedSystemImages()
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NdkList AndroidSdkManager::installedNdkPackages()
|
||||||
|
{
|
||||||
|
AndroidSdkPackageList list = m_d->filteredPackages(AndroidSdkPackage::Installed,
|
||||||
|
AndroidSdkPackage::NDKPackage);
|
||||||
|
return Utils::static_container_cast<Ndk *>(list);
|
||||||
|
}
|
||||||
|
|
||||||
SdkPlatform *AndroidSdkManager::latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state)
|
SdkPlatform *AndroidSdkManager::latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state)
|
||||||
{
|
{
|
||||||
SdkPlatform *result = nullptr;
|
SdkPlatform *result = nullptr;
|
||||||
@@ -601,6 +611,10 @@ void SdkManagerOutputParser::parsePackageData(MarkerTag packageMarker, const QSt
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case MarkerTag::NdkMarker:
|
||||||
|
createPackage(&SdkManagerOutputParser::parseNdkPackage);
|
||||||
|
break;
|
||||||
|
|
||||||
case MarkerTag::ExtrasMarker:
|
case MarkerTag::ExtrasMarker:
|
||||||
createPackage(&SdkManagerOutputParser::parseExtraToolsPackage);
|
createPackage(&SdkManagerOutputParser::parseExtraToolsPackage);
|
||||||
break;
|
break;
|
||||||
@@ -771,6 +785,24 @@ EmulatorTools *SdkManagerOutputParser::parseEmulatorToolsPackage(const QStringLi
|
|||||||
return emulatorTools;
|
return emulatorTools;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ndk *SdkManagerOutputParser::parseNdkPackage(const QStringList &data) const
|
||||||
|
{
|
||||||
|
Ndk *ndk = nullptr;
|
||||||
|
GenericPackageData packageData;
|
||||||
|
if (parseAbstractData(packageData, data, 1, "NDK")) {
|
||||||
|
ndk = new Ndk(packageData.revision, data.at(0));
|
||||||
|
ndk->setDescriptionText(packageData.description);
|
||||||
|
ndk->setDisplayText(packageData.description);
|
||||||
|
ndk->setInstalledLocation(packageData.installedLocation);
|
||||||
|
if (packageData.description == "NDK")
|
||||||
|
ndk->setAsNdkBundle(true);
|
||||||
|
} else {
|
||||||
|
qCDebug(sdkManagerLog) << "NDK: Parsing failed. Minimum required data unavailable:"
|
||||||
|
<< data;
|
||||||
|
}
|
||||||
|
return ndk;
|
||||||
|
}
|
||||||
|
|
||||||
ExtraTools *SdkManagerOutputParser::parseExtraToolsPackage(const QStringList &data) const
|
ExtraTools *SdkManagerOutputParser::parseExtraToolsPackage(const QStringList &data) const
|
||||||
{
|
{
|
||||||
ExtraTools *extraTools = nullptr;
|
ExtraTools *extraTools = nullptr;
|
||||||
|
@@ -70,6 +70,7 @@ public:
|
|||||||
AndroidSdkPackageList availableSdkPackages();
|
AndroidSdkPackageList availableSdkPackages();
|
||||||
AndroidSdkPackageList installedSdkPackages();
|
AndroidSdkPackageList installedSdkPackages();
|
||||||
SystemImageList installedSystemImages();
|
SystemImageList installedSystemImages();
|
||||||
|
NdkList installedNdkPackages();
|
||||||
|
|
||||||
SdkPlatform *latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state
|
SdkPlatform *latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state
|
||||||
= AndroidSdkPackage::Installed);
|
= AndroidSdkPackage::Installed);
|
||||||
|
@@ -273,37 +273,44 @@ bool AndroidSdkModel::setData(const QModelIndex &index, const QVariant &value, i
|
|||||||
void AndroidSdkModel::selectMissingEssentials()
|
void AndroidSdkModel::selectMissingEssentials()
|
||||||
{
|
{
|
||||||
resetSelection();
|
resetSelection();
|
||||||
bool selectPlatformTool = !m_config.adbToolPath().exists();
|
QStringList pendingPkgs(m_config.allEssentials());
|
||||||
bool selectBuildTools = m_config.buildToolsVersion().isNull();
|
|
||||||
auto addTool = [this](QList<const AndroidSdkPackage *>::const_iterator itr) {
|
auto addTool = [this](QList<const AndroidSdkPackage *>::const_iterator itr) {
|
||||||
m_changeState << *itr;
|
if ((*itr)->installedLocation().isEmpty()) {
|
||||||
auto i = index(std::distance(m_tools.cbegin(), itr), 0, index(0, 0));
|
m_changeState << *itr;
|
||||||
emit dataChanged(i, i, {Qt::CheckStateRole});
|
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) {
|
for (auto tool = m_tools.cbegin(); tool != m_tools.cend(); ++tool) {
|
||||||
if (selectPlatformTool && (*tool)->type() == AndroidSdkPackage::PlatformToolsPackage) {
|
if (!pendingPkgs.contains((*tool)->sdkStylePath()))
|
||||||
// Select Platform tools
|
continue;
|
||||||
addTool(tool);
|
|
||||||
selectPlatformTool = false;
|
if ((*tool)->type() == AndroidSdkPackage::PlatformToolsPackage)
|
||||||
}
|
addTool(tool); // Select Platform tools
|
||||||
if (selectBuildTools && (*tool)->type() == AndroidSdkPackage::BuildToolsPackage) {
|
else if ((*tool)->type() == AndroidSdkPackage::BuildToolsPackage)
|
||||||
// Select build tools
|
addTool(tool); // Select build tools
|
||||||
addTool(tool);
|
else if ((*tool)->type() == AndroidSdkPackage::NDKPackage)
|
||||||
selectBuildTools = false;
|
addTool(tool); // Select NDK Bundle
|
||||||
}
|
|
||||||
if (!selectPlatformTool && !selectBuildTools)
|
pendingPkgs.removeOne((*tool)->sdkStylePath());
|
||||||
|
if (pendingPkgs.isEmpty())
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Select SDK platform
|
// Select SDK platform
|
||||||
if (m_sdkManager->installedSdkPlatforms().isEmpty() && !m_sdkPlatforms.isEmpty()) {
|
for (const SdkPlatform *platform : m_sdkPlatforms) {
|
||||||
auto i = index(0, 0, index(1,0));
|
if (pendingPkgs.contains(platform->sdkStylePath()) &&
|
||||||
m_changeState << m_sdkPlatforms.at(0);
|
platform->installedLocation().isEmpty()) {
|
||||||
emit dataChanged(i , i, {Qt::CheckStateRole});
|
auto i = index(0, 0, index(1, 0));
|
||||||
|
m_changeState << platform;
|
||||||
|
emit dataChanged(i, i, {Qt::CheckStateRole});
|
||||||
|
pendingPkgs.removeOne(platform->sdkStylePath());
|
||||||
|
}
|
||||||
|
if (pendingPkgs.isEmpty())
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QList<const AndroidSdkPackage *> AndroidSdkModel::userSelection() const
|
QList<const AndroidSdkPackage *> AndroidSdkModel::userSelection() const
|
||||||
{
|
{
|
||||||
return Utils::toList(m_changeState);
|
return Utils::toList(m_changeState);
|
||||||
|
@@ -294,4 +294,29 @@ AndroidSdkPackage::PackageType ExtraTools::type() const
|
|||||||
return AndroidSdkPackage::ExtraToolsPackage;
|
return AndroidSdkPackage::ExtraToolsPackage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ndk::Ndk(QVersionNumber revision, QString sdkStylePathStr, QObject *parent) :
|
||||||
|
AndroidSdkPackage(revision, sdkStylePathStr, parent)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ndk::isValid() const
|
||||||
|
{
|
||||||
|
return installedLocation().exists();
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidSdkPackage::PackageType Ndk::type() const
|
||||||
|
{
|
||||||
|
return AndroidSdkPackage::NDKPackage;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Ndk::isNdkBundle() const
|
||||||
|
{
|
||||||
|
return m_isBundle;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Ndk::setAsNdkBundle(const bool isBundle)
|
||||||
|
{
|
||||||
|
m_isBundle = isBundle;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Android
|
} // namespace Android
|
||||||
|
@@ -53,9 +53,11 @@ public:
|
|||||||
SdkPlatformPackage = 1 << 4,
|
SdkPlatformPackage = 1 << 4,
|
||||||
SystemImagePackage = 1 << 5,
|
SystemImagePackage = 1 << 5,
|
||||||
EmulatorToolsPackage = 1 << 6,
|
EmulatorToolsPackage = 1 << 6,
|
||||||
ExtraToolsPackage = 1 << 7,
|
NDKPackage = 1 << 7,
|
||||||
|
ExtraToolsPackage = 1 << 8,
|
||||||
AnyValidType = SdkToolsPackage | BuildToolsPackage | PlatformToolsPackage |
|
AnyValidType = SdkToolsPackage | BuildToolsPackage | PlatformToolsPackage |
|
||||||
SdkPlatformPackage | SystemImagePackage | EmulatorToolsPackage | ExtraToolsPackage
|
SdkPlatformPackage | SystemImagePackage | EmulatorToolsPackage | NDKPackage |
|
||||||
|
ExtraToolsPackage
|
||||||
};
|
};
|
||||||
|
|
||||||
enum PackageState {
|
enum PackageState {
|
||||||
@@ -196,6 +198,23 @@ public:
|
|||||||
PackageType type() const override;
|
PackageType type() const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class Ndk : public AndroidSdkPackage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Ndk(QVersionNumber revision, QString sdkStylePathStr, QObject *parent = nullptr);
|
||||||
|
|
||||||
|
// AndroidSdkPackage Overrides
|
||||||
|
bool isValid() const override;
|
||||||
|
PackageType type() const override;
|
||||||
|
|
||||||
|
bool isNdkBundle() const;
|
||||||
|
void setAsNdkBundle(const bool isBundle);
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_isBundle = false;
|
||||||
|
};
|
||||||
|
using NdkList = QList<Ndk *>;
|
||||||
|
|
||||||
class ExtraTools : public AndroidSdkPackage
|
class ExtraTools : public AndroidSdkPackage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@@ -33,6 +33,7 @@
|
|||||||
#include "androidavdmanager.h"
|
#include "androidavdmanager.h"
|
||||||
#include "androidsdkmanager.h"
|
#include "androidsdkmanager.h"
|
||||||
#include "avddialog.h"
|
#include "avddialog.h"
|
||||||
|
#include "androidsdkdownloader.h"
|
||||||
#include "androidsdkmanagerwidget.h"
|
#include "androidsdkmanagerwidget.h"
|
||||||
|
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
@@ -51,6 +52,7 @@
|
|||||||
|
|
||||||
#include <QAbstractTableModel>
|
#include <QAbstractTableModel>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
|
#include <QDir>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QList>
|
#include <QList>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
@@ -104,6 +106,7 @@ private:
|
|||||||
void validateJdk();
|
void validateJdk();
|
||||||
Utils::FilePath findJdkInCommonPaths();
|
Utils::FilePath findJdkInCommonPaths();
|
||||||
void validateNdk();
|
void validateNdk();
|
||||||
|
void updateNdkList();
|
||||||
void onSdkPathChanged();
|
void onSdkPathChanged();
|
||||||
void validateSdk();
|
void validateSdk();
|
||||||
void openSDKDownloadUrl();
|
void openSDKDownloadUrl();
|
||||||
@@ -125,6 +128,11 @@ private:
|
|||||||
void enableAvdControls();
|
void enableAvdControls();
|
||||||
void disableAvdControls();
|
void disableAvdControls();
|
||||||
|
|
||||||
|
void downloadSdk();
|
||||||
|
bool allEssentialsInstalled();
|
||||||
|
bool sdkToolsOk() const;
|
||||||
|
Utils::FilePath getDefaultSdkPath();
|
||||||
|
|
||||||
Ui_AndroidSettingsWidget *m_ui;
|
Ui_AndroidSettingsWidget *m_ui;
|
||||||
AndroidSdkManagerWidget *m_sdkManagerWidget = nullptr;
|
AndroidSdkManagerWidget *m_sdkManagerWidget = nullptr;
|
||||||
AndroidConfig m_androidConfig;
|
AndroidConfig m_androidConfig;
|
||||||
@@ -150,6 +158,7 @@ enum AndroidValidation {
|
|||||||
BuildToolsInstalledRow,
|
BuildToolsInstalledRow,
|
||||||
SdkManagerSuccessfulRow,
|
SdkManagerSuccessfulRow,
|
||||||
PlatformSdkInstalledRow,
|
PlatformSdkInstalledRow,
|
||||||
|
AllEssentialsInstalledRow,
|
||||||
NdkPathExistsRow,
|
NdkPathExistsRow,
|
||||||
NdkDirStructureRow,
|
NdkDirStructureRow,
|
||||||
NdkinstallDirOkRow
|
NdkinstallDirOkRow
|
||||||
@@ -302,6 +311,37 @@ int AvdModel::columnCount(const QModelIndex &/*parent*/) const
|
|||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Utils::FilePath AndroidSettingsWidget::getDefaultSdkPath()
|
||||||
|
{
|
||||||
|
QString sdkFromEnvVar = QString::fromLocal8Bit(getenv("ANDROID_SDK_ROOT"));
|
||||||
|
if (!sdkFromEnvVar.isEmpty())
|
||||||
|
return Utils::FilePath::fromString(sdkFromEnvVar);
|
||||||
|
|
||||||
|
// Set default path of SDK as used by Android Studio
|
||||||
|
if (Utils::HostOsInfo::isMacHost()) {
|
||||||
|
return Utils::FilePath::fromString(
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation)
|
||||||
|
+ "/../Android/sdk");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Utils::HostOsInfo::isWindowsHost()) {
|
||||||
|
return Utils::FilePath::fromString(
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/Android/sdk");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Utils::FilePath::fromString(
|
||||||
|
QStandardPaths::writableLocation(QStandardPaths::HomeLocation) + "/Android/Sdk");
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidSettingsWidget::updateNdkList()
|
||||||
|
{
|
||||||
|
m_ui->ndkListComboBox->clear();
|
||||||
|
QString currentNdk = m_androidConfig.ndkLocation().toString();
|
||||||
|
for (const Ndk *ndk : m_sdkManager->installedNdkPackages())
|
||||||
|
m_ui->ndkListComboBox->addItem(ndk->installedLocation().toString());
|
||||||
|
m_ui->ndkListComboBox->setCurrentText(currentNdk);
|
||||||
|
}
|
||||||
|
|
||||||
AndroidSettingsWidget::AndroidSettingsWidget()
|
AndroidSettingsWidget::AndroidSettingsWidget()
|
||||||
: m_ui(new Ui_AndroidSettingsWidget),
|
: m_ui(new Ui_AndroidSettingsWidget),
|
||||||
m_androidConfig(AndroidConfigurations::currentConfig()),
|
m_androidConfig(AndroidConfigurations::currentConfig()),
|
||||||
@@ -339,28 +379,36 @@ AndroidSettingsWidget::AndroidSettingsWidget()
|
|||||||
androidValidationPoints[PlatformToolsInstalledRow] = tr("Platform tools installed.");
|
androidValidationPoints[PlatformToolsInstalledRow] = tr("Platform tools installed.");
|
||||||
androidValidationPoints[SdkManagerSuccessfulRow] = tr(
|
androidValidationPoints[SdkManagerSuccessfulRow] = tr(
|
||||||
"SDK manager runs (requires exactly Java 1.8).");
|
"SDK manager runs (requires exactly Java 1.8).");
|
||||||
|
androidValidationPoints[AllEssentialsInstalledRow] = tr(
|
||||||
|
"All essential packages installed for all installed Qt versions.");
|
||||||
androidValidationPoints[BuildToolsInstalledRow] = tr("Build tools installed.");
|
androidValidationPoints[BuildToolsInstalledRow] = tr("Build tools installed.");
|
||||||
androidValidationPoints[PlatformSdkInstalledRow] = tr("Platform SDK installed.");
|
androidValidationPoints[PlatformSdkInstalledRow] = tr("Platform SDK installed.");
|
||||||
androidValidationPoints[NdkPathExistsRow] = tr("Android NDK path exists.");
|
androidValidationPoints[NdkPathExistsRow] = tr("Default Android NDK path exists.");
|
||||||
androidValidationPoints[NdkDirStructureRow] = tr("Android NDK directory structure is correct.");
|
androidValidationPoints[NdkDirStructureRow] = tr("Default Android NDK directory structure is correct.");
|
||||||
androidValidationPoints[NdkinstallDirOkRow] = tr("Android NDK installed into a path without "
|
androidValidationPoints[NdkinstallDirOkRow] = tr("Default Android NDK installed into a path without "
|
||||||
"spaces.");
|
"spaces.");
|
||||||
auto androidSummary = new SummaryWidget(androidValidationPoints, tr("Android settings are OK."),
|
auto androidSummary = new SummaryWidget(androidValidationPoints, tr("Android settings are OK."),
|
||||||
tr("Android settings have errors."),
|
tr("Android settings have errors."),
|
||||||
m_ui->androidDetailsWidget);
|
m_ui->androidDetailsWidget);
|
||||||
m_ui->androidDetailsWidget->setWidget(androidSummary);
|
m_ui->androidDetailsWidget->setWidget(androidSummary);
|
||||||
|
|
||||||
m_ui->SDKLocationPathChooser->setFileName(m_androidConfig.sdkLocation());
|
connect(m_ui->OpenJDKLocationPathChooser, &Utils::PathChooser::rawPathChanged,
|
||||||
m_ui->SDKLocationPathChooser->setPromptDialogTitle(tr("Select Android SDK folder"));
|
this, &AndroidSettingsWidget::validateJdk);
|
||||||
m_ui->NDKLocationPathChooser->setFileName(m_androidConfig.ndkLocation());
|
|
||||||
m_ui->NDKLocationPathChooser->setPromptDialogTitle(tr("Select Android NDK folder"));
|
|
||||||
|
|
||||||
Utils::FilePath currentJdkPath = m_androidConfig.openJDKLocation();
|
Utils::FilePath currentJdkPath = m_androidConfig.openJDKLocation();
|
||||||
if (currentJdkPath.isEmpty())
|
if (currentJdkPath.isEmpty())
|
||||||
currentJdkPath = findJdkInCommonPaths();
|
currentJdkPath = findJdkInCommonPaths();
|
||||||
|
|
||||||
m_ui->OpenJDKLocationPathChooser->setFileName(currentJdkPath);
|
m_ui->OpenJDKLocationPathChooser->setFileName(currentJdkPath);
|
||||||
m_ui->OpenJDKLocationPathChooser->setPromptDialogTitle(tr("Select JDK Path"));
|
m_ui->OpenJDKLocationPathChooser->setPromptDialogTitle(tr("Select JDK Path"));
|
||||||
|
|
||||||
|
connect(m_ui->SDKLocationPathChooser, &Utils::PathChooser::rawPathChanged,
|
||||||
|
this, &AndroidSettingsWidget::onSdkPathChanged);
|
||||||
|
Utils::FilePath currentSDKPath = m_androidConfig.sdkLocation();
|
||||||
|
if (currentSDKPath.isEmpty())
|
||||||
|
currentSDKPath = getDefaultSdkPath();
|
||||||
|
|
||||||
|
m_ui->SDKLocationPathChooser->setFileName(currentSDKPath);
|
||||||
|
m_ui->SDKLocationPathChooser->setPromptDialogTitle(tr("Select Android SDK folder"));
|
||||||
|
|
||||||
m_ui->DataPartitionSizeSpinBox->setValue(m_androidConfig.partitionSize());
|
m_ui->DataPartitionSizeSpinBox->setValue(m_androidConfig.partitionSize());
|
||||||
m_ui->CreateKitCheckBox->setChecked(m_androidConfig.automaticKitCreation());
|
m_ui->CreateKitCheckBox->setChecked(m_androidConfig.automaticKitCreation());
|
||||||
m_ui->AVDTableView->setModel(&m_AVDModel);
|
m_ui->AVDTableView->setModel(&m_AVDModel);
|
||||||
@@ -373,19 +421,15 @@ AndroidSettingsWidget::AndroidSettingsWidget()
|
|||||||
m_ui->downloadSDKToolButton->setIcon(downloadIcon);
|
m_ui->downloadSDKToolButton->setIcon(downloadIcon);
|
||||||
m_ui->downloadNDKToolButton->setIcon(downloadIcon);
|
m_ui->downloadNDKToolButton->setIcon(downloadIcon);
|
||||||
m_ui->downloadOpenJDKToolButton->setIcon(downloadIcon);
|
m_ui->downloadOpenJDKToolButton->setIcon(downloadIcon);
|
||||||
|
m_ui->sdkToolsAutoDownloadButton->setIcon(Utils::Icons::RELOAD.icon());
|
||||||
|
|
||||||
|
connect(m_ui->ndkListComboBox, QOverload<const QString &>::of(&QComboBox::currentIndexChanged),
|
||||||
|
[this](const QString) { validateNdk(); });
|
||||||
connect(&m_virtualDevicesWatcher, &QFutureWatcherBase::finished,
|
connect(&m_virtualDevicesWatcher, &QFutureWatcherBase::finished,
|
||||||
this, &AndroidSettingsWidget::updateAvds);
|
this, &AndroidSettingsWidget::updateAvds);
|
||||||
connect(m_ui->AVDRefreshPushButton, &QAbstractButton::clicked,
|
connect(m_ui->AVDRefreshPushButton, &QAbstractButton::clicked,
|
||||||
this, &AndroidSettingsWidget::startUpdateAvd);
|
this, &AndroidSettingsWidget::startUpdateAvd);
|
||||||
connect(&m_futureWatcher, &QFutureWatcherBase::finished,
|
connect(&m_futureWatcher, &QFutureWatcherBase::finished, this, &AndroidSettingsWidget::avdAdded);
|
||||||
this, &AndroidSettingsWidget::avdAdded);
|
|
||||||
connect(m_ui->NDKLocationPathChooser, &Utils::PathChooser::rawPathChanged,
|
|
||||||
this, &AndroidSettingsWidget::validateNdk);
|
|
||||||
connect(m_ui->SDKLocationPathChooser, &Utils::PathChooser::rawPathChanged,
|
|
||||||
this, &AndroidSettingsWidget::onSdkPathChanged);
|
|
||||||
connect(m_ui->OpenJDKLocationPathChooser, &Utils::PathChooser::rawPathChanged,
|
|
||||||
this, &AndroidSettingsWidget::validateJdk);
|
|
||||||
connect(m_ui->AVDAddPushButton, &QAbstractButton::clicked,
|
connect(m_ui->AVDAddPushButton, &QAbstractButton::clicked,
|
||||||
this, &AndroidSettingsWidget::addAVD);
|
this, &AndroidSettingsWidget::addAVD);
|
||||||
connect(m_ui->AVDRemovePushButton, &QAbstractButton::clicked,
|
connect(m_ui->AVDRemovePushButton, &QAbstractButton::clicked,
|
||||||
@@ -402,20 +446,38 @@ AndroidSettingsWidget::AndroidSettingsWidget()
|
|||||||
this, &AndroidSettingsWidget::manageAVD);
|
this, &AndroidSettingsWidget::manageAVD);
|
||||||
connect(m_ui->CreateKitCheckBox, &QAbstractButton::toggled,
|
connect(m_ui->CreateKitCheckBox, &QAbstractButton::toggled,
|
||||||
this, &AndroidSettingsWidget::createKitToggled);
|
this, &AndroidSettingsWidget::createKitToggled);
|
||||||
connect(m_ui->downloadSDKToolButton, &QAbstractButton::clicked,
|
|
||||||
this, &AndroidSettingsWidget::openSDKDownloadUrl);
|
|
||||||
connect(m_ui->downloadNDKToolButton, &QAbstractButton::clicked,
|
connect(m_ui->downloadNDKToolButton, &QAbstractButton::clicked,
|
||||||
this, &AndroidSettingsWidget::openNDKDownloadUrl);
|
this, &AndroidSettingsWidget::openNDKDownloadUrl);
|
||||||
|
connect(m_ui->downloadSDKToolButton, &QAbstractButton::clicked,
|
||||||
|
this, &AndroidSettingsWidget::openSDKDownloadUrl);
|
||||||
connect(m_ui->downloadOpenJDKToolButton, &QAbstractButton::clicked,
|
connect(m_ui->downloadOpenJDKToolButton, &QAbstractButton::clicked,
|
||||||
this, &AndroidSettingsWidget::openOpenJDKDownloadUrl);
|
this, &AndroidSettingsWidget::openOpenJDKDownloadUrl);
|
||||||
// Validate SDK again after any change in SDK packages.
|
// Validate SDK again after any change in SDK packages.
|
||||||
connect(m_sdkManager.get(), &AndroidSdkManager::packageReloadFinished,
|
connect(m_sdkManager.get(),
|
||||||
this, &AndroidSettingsWidget::validateSdk);
|
&AndroidSdkManager::packageReloadFinished,
|
||||||
validateJdk();
|
this,
|
||||||
validateNdk();
|
&AndroidSettingsWidget::validateSdk);
|
||||||
// Reloading SDK packages is still synchronous. Use zero timer to let settings dialog open
|
connect(m_ui->sdkToolsAutoDownloadButton, &QAbstractButton::clicked, this, [this]() {
|
||||||
|
if (sdkToolsOk()) {
|
||||||
|
QMessageBox::warning(this, AndroidSdkDownloader::dialogTitle(),
|
||||||
|
tr("The selected path already has a valid SDK Tools package."));
|
||||||
|
validateSdk();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
downloadSdk();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto startOneShot = QSharedPointer<QMetaObject::Connection>::create();
|
||||||
|
*startOneShot = connect(m_sdkManager.get(),
|
||||||
|
&AndroidSdkManager::packageReloadFinished, [this, startOneShot] {
|
||||||
|
QObject::disconnect(*startOneShot);
|
||||||
|
if (!sdkToolsOk())
|
||||||
|
downloadSdk();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reloading SDK packages (force) is still synchronous. Use zero timer to let settings dialog open
|
||||||
// first.
|
// first.
|
||||||
QTimer::singleShot(0, std::bind(&AndroidSdkManager::reloadPackages, m_sdkManager.get(), false));
|
QTimer::singleShot(0, std::bind(&AndroidSdkManager::reloadPackages, m_sdkManager.get(), true));
|
||||||
|
|
||||||
startUpdateAvd();
|
startUpdateAvd();
|
||||||
}
|
}
|
||||||
@@ -521,11 +583,11 @@ Utils::FilePath AndroidSettingsWidget::findJdkInCommonPaths()
|
|||||||
|
|
||||||
void AndroidSettingsWidget::validateNdk()
|
void AndroidSettingsWidget::validateNdk()
|
||||||
{
|
{
|
||||||
auto ndkPath = Utils::FilePath::fromUserInput(m_ui->NDKLocationPathChooser->rawPath());
|
auto ndkPath = Utils::FilePath::fromString(m_ui->ndkListComboBox->currentText());
|
||||||
m_androidConfig.setNdkLocation(ndkPath);
|
m_androidConfig.setNdkLocation(ndkPath);
|
||||||
|
|
||||||
auto summaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
|
auto summaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
|
||||||
summaryWidget->setPointValid(NdkPathExistsRow, m_androidConfig.ndkLocation().exists());
|
summaryWidget->setPointValid(NdkPathExistsRow, ndkPath.exists());
|
||||||
|
|
||||||
const Utils::FilePath ndkPlatformsDir = ndkPath.pathAppended("platforms");
|
const Utils::FilePath ndkPlatformsDir = ndkPath.pathAppended("platforms");
|
||||||
const Utils::FilePath ndkToolChainsDir = ndkPath.pathAppended("toolchains");
|
const Utils::FilePath ndkToolChainsDir = ndkPath.pathAppended("toolchains");
|
||||||
@@ -550,6 +612,9 @@ void AndroidSettingsWidget::onSdkPathChanged()
|
|||||||
|
|
||||||
void AndroidSettingsWidget::validateSdk()
|
void AndroidSettingsWidget::validateSdk()
|
||||||
{
|
{
|
||||||
|
auto sdkPath = Utils::FilePath::fromString(m_ui->SDKLocationPathChooser->rawPath());
|
||||||
|
m_androidConfig.setSdkLocation(sdkPath);
|
||||||
|
|
||||||
auto summaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
|
auto summaryWidget = static_cast<SummaryWidget *>(m_ui->androidDetailsWidget->widget());
|
||||||
summaryWidget->setPointValid(SdkPathExistsRow, m_androidConfig.sdkLocation().exists());
|
summaryWidget->setPointValid(SdkPathExistsRow, m_androidConfig.sdkLocation().exists());
|
||||||
summaryWidget->setPointValid(SdkPathWritableRow, m_androidConfig.sdkLocation().isWritablePath());
|
summaryWidget->setPointValid(SdkPathWritableRow, m_androidConfig.sdkLocation().isWritablePath());
|
||||||
@@ -559,19 +624,21 @@ void AndroidSettingsWidget::validateSdk()
|
|||||||
m_androidConfig.adbToolPath().exists());
|
m_androidConfig.adbToolPath().exists());
|
||||||
summaryWidget->setPointValid(BuildToolsInstalledRow,
|
summaryWidget->setPointValid(BuildToolsInstalledRow,
|
||||||
!m_androidConfig.buildToolsVersion().isNull());
|
!m_androidConfig.buildToolsVersion().isNull());
|
||||||
|
|
||||||
summaryWidget->setPointValid(SdkManagerSuccessfulRow, m_sdkManager->packageListingSuccessful());
|
summaryWidget->setPointValid(SdkManagerSuccessfulRow, m_sdkManager->packageListingSuccessful());
|
||||||
// installedSdkPlatforms should not trigger a package reload as validate SDK is only called
|
// installedSdkPlatforms should not trigger a package reload as validate SDK is only called
|
||||||
// after AndroidSdkManager::packageReloadFinished.
|
// after AndroidSdkManager::packageReloadFinished.
|
||||||
summaryWidget->setPointValid(PlatformSdkInstalledRow,
|
summaryWidget->setPointValid(PlatformSdkInstalledRow,
|
||||||
!m_sdkManager->installedSdkPlatforms().isEmpty());
|
!m_sdkManager->installedSdkPlatforms().isEmpty());
|
||||||
|
|
||||||
|
summaryWidget->setPointValid(AllEssentialsInstalledRow, allEssentialsInstalled());
|
||||||
updateUI();
|
updateUI();
|
||||||
bool sdkToolsOk = summaryWidget->rowsOk(
|
bool sdkToolsOk = summaryWidget->rowsOk(
|
||||||
{SdkPathExistsRow, SdkPathWritableRow, SdkToolsInstalledRow, SdkManagerSuccessfulRow});
|
{SdkPathExistsRow, SdkPathWritableRow, SdkToolsInstalledRow, SdkManagerSuccessfulRow});
|
||||||
bool componentsOk = summaryWidget->rowsOk({PlatformToolsInstalledRow,
|
bool componentsOk = summaryWidget->rowsOk({PlatformToolsInstalledRow,
|
||||||
BuildToolsInstalledRow,
|
BuildToolsInstalledRow,
|
||||||
PlatformSdkInstalledRow});
|
PlatformSdkInstalledRow,
|
||||||
|
AllEssentialsInstalledRow});
|
||||||
|
m_androidConfig.setSdkFullyConfigured(sdkToolsOk && componentsOk);
|
||||||
if (sdkToolsOk && !componentsOk && !m_androidConfig.useNativeUiTools()) {
|
if (sdkToolsOk && !componentsOk && !m_androidConfig.useNativeUiTools()) {
|
||||||
// Ask user to install essential SDK components. Works only for sdk tools version >= 26.0.0
|
// 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 "
|
QString message = tr("Android SDK installation is missing necessary packages. Do you "
|
||||||
@@ -583,6 +650,9 @@ void AndroidSettingsWidget::validateSdk()
|
|||||||
m_sdkManagerWidget->installEssentials();
|
m_sdkManagerWidget->installEssentials();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
updateNdkList();
|
||||||
|
validateNdk();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::openSDKDownloadUrl()
|
void AndroidSettingsWidget::openSDKDownloadUrl()
|
||||||
@@ -675,7 +745,7 @@ void AndroidSettingsWidget::updateUI()
|
|||||||
m_ui->sdkManagerTab->setEnabled(sdkToolsOk);
|
m_ui->sdkManagerTab->setEnabled(sdkToolsOk);
|
||||||
m_sdkManagerWidget->setSdkManagerControlsEnabled(!m_androidConfig.useNativeUiTools());
|
m_sdkManagerWidget->setSdkManagerControlsEnabled(!m_androidConfig.useNativeUiTools());
|
||||||
|
|
||||||
auto infoText = tr("(SDK Version: %1, NDK Version: %2)")
|
auto infoText = tr("(SDK Version: %1, NDK Bundle Version: %2)")
|
||||||
.arg(m_androidConfig.sdkToolsVersion().toString())
|
.arg(m_androidConfig.sdkToolsVersion().toString())
|
||||||
.arg(m_androidConfig.ndkVersion().toString());
|
.arg(m_androidConfig.ndkVersion().toString());
|
||||||
androidSummaryWidget->setInfoText(androidSetupOk ? infoText : "");
|
androidSummaryWidget->setInfoText(androidSetupOk ? infoText : "");
|
||||||
@@ -699,6 +769,57 @@ void AndroidSettingsWidget::manageAVD()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AndroidSettingsWidget::downloadSdk()
|
||||||
|
{
|
||||||
|
QString message(tr("Android SDK Tools package is not installed. Do you want to download it?\n"
|
||||||
|
"The final location: ")
|
||||||
|
+ QDir::toNativeSeparators(m_ui->SDKLocationPathChooser->rawPath()));
|
||||||
|
auto userInput = QMessageBox::information(this, AndroidSdkDownloader::dialogTitle(),
|
||||||
|
message, QMessageBox::Yes | QMessageBox::No);
|
||||||
|
if (userInput == QMessageBox::Yes) {
|
||||||
|
auto javaSummaryWidget = static_cast<SummaryWidget *>(m_ui->javaDetailsWidget->widget());
|
||||||
|
if (javaSummaryWidget->allRowsOk()) {
|
||||||
|
auto javaPath = Utils::FilePath::fromUserInput(m_ui->OpenJDKLocationPathChooser->rawPath());
|
||||||
|
AndroidSdkDownloader *sdkDownloader = new AndroidSdkDownloader(
|
||||||
|
m_androidConfig.sdkToolsUrl(),
|
||||||
|
m_androidConfig.getSdkToolsSha256());
|
||||||
|
sdkDownloader->downloadAndExtractSdk(javaPath.toString(),
|
||||||
|
m_ui->SDKLocationPathChooser->path());
|
||||||
|
|
||||||
|
connect(sdkDownloader, &AndroidSdkDownloader::sdkExtracted, this, [this]() {
|
||||||
|
m_sdkManager->reloadPackages(true);
|
||||||
|
apply();
|
||||||
|
});
|
||||||
|
|
||||||
|
auto showErrorDialog = [this](const QString &error) {
|
||||||
|
QMessageBox::warning(this, AndroidSdkDownloader::dialogTitle(), error);
|
||||||
|
};
|
||||||
|
|
||||||
|
connect(sdkDownloader, &AndroidSdkDownloader::sdkDownloaderError, this, showErrorDialog);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidSettingsWidget::allEssentialsInstalled()
|
||||||
|
{
|
||||||
|
QStringList essentialPkgs(m_androidConfig.allEssentials());
|
||||||
|
for (const AndroidSdkPackage *pkg : m_sdkManager->installedSdkPackages()) {
|
||||||
|
if (essentialPkgs.contains(pkg->sdkStylePath()))
|
||||||
|
essentialPkgs.removeOne(pkg->sdkStylePath());
|
||||||
|
if (essentialPkgs.isEmpty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return essentialPkgs.isEmpty() ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidSettingsWidget::sdkToolsOk() const
|
||||||
|
{
|
||||||
|
bool exists = m_androidConfig.sdkLocation().exists();
|
||||||
|
bool writable = m_androidConfig.sdkLocation().isWritablePath();
|
||||||
|
bool sdkToolsExist = !m_androidConfig.sdkToolsVersion().isNull();
|
||||||
|
return exists && writable && sdkToolsExist;
|
||||||
|
}
|
||||||
|
|
||||||
// AndroidSettingsPage
|
// AndroidSettingsPage
|
||||||
|
|
||||||
AndroidSettingsPage::AndroidSettingsPage()
|
AndroidSettingsPage::AndroidSettingsPage()
|
||||||
|
@@ -90,42 +90,6 @@
|
|||||||
<string>Android Settings</string>
|
<string>Android Settings</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QGridLayout" name="gridLayout_3">
|
<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>
|
|
||||||
</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">
|
<item row="0" column="0">
|
||||||
<widget class="QLabel" name="SDKLocationLabel">
|
<widget class="QLabel" name="SDKLocationLabel">
|
||||||
<property name="sizePolicy">
|
<property name="sizePolicy">
|
||||||
@@ -142,16 +106,53 @@
|
|||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="0" column="2">
|
<item row="0" column="4">
|
||||||
<widget class="QToolButton" name="downloadSDKToolButton">
|
<widget class="QToolButton" name="downloadSDKToolButton">
|
||||||
<property name="toolTip">
|
<property name="toolTip">
|
||||||
<string>Download Android SDK</string>
|
<string>Download Android SDK</string>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
<item row="2" column="0" colspan="3">
|
<item row="1" column="0">
|
||||||
|
<widget class="QLabel" name="ndkComboBoxLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Android NDK list:</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="3">
|
||||||
|
<widget class="QToolButton" name="sdkToolsAutoDownloadButton">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Automatically download Android SDK Tools to selected location.</string>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="2" column="0" colspan="5">
|
||||||
<widget class="Utils::DetailsWidget" name="androidDetailsWidget" native="true"/>
|
<widget class="Utils::DetailsWidget" name="androidDetailsWidget" native="true"/>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="1" column="4">
|
||||||
|
<widget class="QToolButton" name="downloadNDKToolButton">
|
||||||
|
<property name="text">
|
||||||
|
<string/>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="0" column="2">
|
||||||
|
<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="QComboBox" name="ndkListComboBox"/>
|
||||||
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
Reference in New Issue
Block a user