diff --git a/share/qtcreator/android/sdk_definitions.json b/share/qtcreator/android/sdk_definitions.json index 56ac6b4f47d..76e67c9fced 100644 --- a/share/qtcreator/android/sdk_definitions.json +++ b/share/qtcreator/android/sdk_definitions.json @@ -1,12 +1,12 @@ { "common": { "sdk_tools_url": { - "linux": "https://dl.google.com/android/repository/commandlinetools-linux-6609375_latest.zip", - "linux_sha256": "89f308315e041c93a37a79e0627c47f21d5c5edbe5e80ea8dc0aac8a649e0e92", - "windows": "https://dl.google.com/android/repository/commandlinetools-win-6609375_latest.zip", - "windows_sha256": "40bba20275180194bebf89bb58c74d712bb93cc401f36bd2f8f32383acf9826c", - "mac": "https://dl.google.com/android/repository/commandlinetools-mac-6609375_latest.zip", - "mac_sha256": "2c3822db1c916655223e5ee8ce0fbf6b73d0b99012045c9dc8eaa6a5736c0c55" + "linux": "https://dl.google.com/android/repository/commandlinetools-linux-8092744_latest.zip", + "linux_sha256": "d71f75333d79c9c6ef5c39d3456c6c58c613de30e6a751ea0dbd433e8f8b9cbf", + "windows": "https://dl.google.com/android/repository/commandlinetools-win-8092744_latest.zip", + "windows_sha256": "5de99ed67cb2e30fe443baf8b282d1b0b6247d0c25c6d888a7e8657b3b35c281", + "mac": "https://dl.google.com/android/repository/commandlinetools-mac-8092744_latest.zip", + "mac_sha256": "1de25523d595198d29666f9976eed65d99bbc5e4a3e8e48e5d6c98bb7e9030cc" }, "sdk_essential_packages": { "default": ["platform-tools", "platforms;android-31", "cmdline-tools;latest"], diff --git a/src/plugins/android/androidbuildapkstep.cpp b/src/plugins/android/androidbuildapkstep.cpp index 5464c09c83e..b53cf3b6f7a 100644 --- a/src/plugins/android/androidbuildapkstep.cpp +++ b/src/plugins/android/androidbuildapkstep.cpp @@ -535,7 +535,7 @@ bool AndroidBuildApkStep::init() const QVersionNumber sdkToolsVersion = AndroidConfigurations::currentConfig().sdkToolsVersion(); if (sdkToolsVersion >= QVersionNumber(25, 3, 0) - || AndroidConfigurations::currentConfig().isCmdlineSdkToolsInstalled()) { + && AndroidConfigurations::currentConfig().preCmdlineSdkToolsInstalled()) { if (!version->sourcePath().pathAppended("src/3rdparty/gradle").exists()) { const QString error = tr("The installed SDK tools version (%1) does not include Gradle " diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index 15f525a5592..c3702fa2763 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -474,9 +474,12 @@ QString AndroidConfig::apiLevelNameFor(const SdkPlatform *platform) QString("android-%1").arg(platform->apiLevel()) : ""; } -bool AndroidConfig::isCmdlineSdkToolsInstalled() const +// This is checking for the SDK tools [*] that were deprecated in favor of +// the command-line tools. +// See https://developer.android.com/studio/releases/sdk-tools +bool AndroidConfig::preCmdlineSdkToolsInstalled() const { - QString toolPath("cmdline-tools/latest/bin/sdkmanager"); + QString toolPath("tools/bin/sdkmanager"); if (HostOsInfo::isWindowsHost()) toolPath += ANDROID_BAT_SUFFIX; @@ -491,38 +494,51 @@ FilePath AndroidConfig::adbToolPath() const FilePath AndroidConfig::emulatorToolPath() const { QString relativePath = "emulator/emulator"; - if (sdkToolsVersion() < QVersionNumber(25, 3, 0) && !isCmdlineSdkToolsInstalled()) + if (sdkToolsVersion() < QVersionNumber(25, 3, 0) && preCmdlineSdkToolsInstalled()) relativePath = "tools/emulator"; return m_sdkLocation / (relativePath + QTC_HOST_EXE_SUFFIX); } FilePath AndroidConfig::sdkManagerToolPath() const { - QStringList sdkmanagerPaths = {"cmdline-tools/latest/bin/sdkmanager", - "tools/bin/sdkmanager"}; + const QStringList sdkmanagerPaths = { + QString(Constants::cmdlineToolsName).append("/latest/bin/sdkmanager"), + "tools/bin/sdkmanager"}; - for (QString &toolPath : sdkmanagerPaths) { + for (const QString &toolPath : sdkmanagerPaths) { + QString toolPathWithSuffix = toolPath; if (HostOsInfo::isWindowsHost()) - toolPath += ANDROID_BAT_SUFFIX; - - const FilePath sdkmanagerPath = m_sdkLocation / toolPath; + toolPathWithSuffix += ANDROID_BAT_SUFFIX; + const FilePath sdkmanagerPath = m_sdkLocation / toolPathWithSuffix; if (sdkmanagerPath.exists()) return sdkmanagerPath; } + // If it's a first time install use the path of Constants::cmdlineToolsName temporary download + const FilePath tmpSdkPath = m_temporarySdkToolsPath; + if (!tmpSdkPath.isEmpty()) { + QString suffix = "bin/sdkmanager"; + if (HostOsInfo::isWindowsHost()) + suffix += ANDROID_BAT_SUFFIX; + const FilePath tmpsdkManagerPath = tmpSdkPath.pathAppended(suffix); + if (tmpsdkManagerPath.exists()) + return tmpsdkManagerPath; + } + return FilePath(); } FilePath AndroidConfig::avdManagerToolPath() const { - QStringList sdkmanagerPaths = {"cmdline-tools/latest/bin/avdmanager", - "tools/bin/avdmanager"}; + const QStringList sdkmanagerPaths = { + QString(Constants::cmdlineToolsName).append("/latest/bin/avdmanager"), + "tools/bin/avdmanager"}; - for (QString &toolPath : sdkmanagerPaths) { + for (const QString &toolPath : sdkmanagerPaths) { + QString toolPathWithSuffix = toolPath; if (HostOsInfo::isWindowsHost()) - toolPath += ANDROID_BAT_SUFFIX; - - const FilePath sdkmanagerPath = m_sdkLocation / toolPath; + toolPathWithSuffix += ANDROID_BAT_SUFFIX; + const FilePath sdkmanagerPath = m_sdkLocation / toolPathWithSuffix; if (sdkmanagerPath.exists()) return sdkmanagerPath; } @@ -530,6 +546,34 @@ FilePath AndroidConfig::avdManagerToolPath() const return FilePath(); } +void AndroidConfig::setTemporarySdkToolsPath(const Utils::FilePath &path) +{ + m_temporarySdkToolsPath = path; +} + +FilePath AndroidConfig::sdkToolsVersionPath() const +{ + const QStringList sdkVersionPaths = { + QString(Constants::cmdlineToolsName).append("/latest/source.properties"), + "tools/source.properties"}; + + for (const QString &versionPath : sdkVersionPaths) { + const FilePath sdkVersionPath = m_sdkLocation / versionPath; + if (sdkVersionPath.exists()) + return sdkVersionPath; + } + + // If it's a first time install use the path of Constants::cmdlineToolsName temporary download + const FilePath tmpSdkPath = m_temporarySdkToolsPath; + if (!tmpSdkPath.isEmpty()) { + const FilePath sdkVersionPath = tmpSdkPath.pathAppended("source.properties"); + if (sdkVersionPath.exists()) + return sdkVersionPath; + } + + return FilePath(); +} + FilePath AndroidConfig::toolchainPathFromNdk(const FilePath &ndkLocation, OsType hostOs) { const FilePath tcPath = ndkLocation / "toolchains/"; @@ -861,18 +905,12 @@ void AndroidConfig::setSdkLocation(const FilePath &sdkLocation) QVersionNumber AndroidConfig::sdkToolsVersion() const { - QVersionNumber version; - if (m_sdkLocation.exists()) { - FilePath sdkToolsPropertiesPath; - if (isCmdlineSdkToolsInstalled()) - sdkToolsPropertiesPath = m_sdkLocation / "cmdline-tools/latest/source.properties"; - else - sdkToolsPropertiesPath = m_sdkLocation / "tools/source.properties"; - QSettings settings(sdkToolsPropertiesPath.toString(), QSettings::IniFormat); - auto versionStr = settings.value(sdkToolsVersionKey).toString(); - version = QVersionNumber::fromString(versionStr); - } - return version; + if (!m_sdkLocation.exists()) + return {}; + + const FilePath sdkToolsPropertiesPath = sdkToolsVersionPath(); + const QSettings settings(sdkToolsPropertiesPath.toString(), QSettings::IniFormat); + return QVersionNumber::fromString(settings.value(sdkToolsVersionKey).toString()); } QVersionNumber AndroidConfig::buildToolsVersion() const diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index e1fe859e54f..c9726370eef 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -96,6 +96,7 @@ public: Utils::FilePath sdkLocation() const; void setSdkLocation(const Utils::FilePath &sdkLocation); QVersionNumber sdkToolsVersion() const; + Utils::FilePath sdkToolsVersionPath() const; QVersionNumber buildToolsVersion() const; QStringList sdkManagerToolArgs() const; void setSdkManagerToolArgs(const QStringList &args); @@ -134,6 +135,8 @@ public: Utils::FilePath sdkManagerToolPath() const; Utils::FilePath avdManagerToolPath() const; + void setTemporarySdkToolsPath(const Utils::FilePath &path); + Utils::FilePath toolchainPath(const QtSupport::QtVersion *qtVersion) const; static Utils::FilePath toolchainPathFromNdk(const Utils::FilePath &ndkLocation, Utils::OsType hostOs = Utils::HostOsInfo::hostOs()); @@ -158,7 +161,7 @@ public: QString getProductModel(const QString &device) const; bool isConnected(const QString &serialNumber) const; - bool isCmdlineSdkToolsInstalled() const; + bool preCmdlineSdkToolsInstalled() const; bool sdkFullyConfigured() const { return m_sdkFullyConfigured; } void setSdkFullyConfigured(bool allEssentialsInstalled) { m_sdkFullyConfigured = allEssentialsInstalled; } @@ -188,6 +191,7 @@ private: QList availableNdkPlatforms(const QtSupport::QtVersion *qtVersion) const; Utils::FilePath m_sdkLocation; + Utils::FilePath m_temporarySdkToolsPath; QStringList m_sdkManagerToolArgs; Utils::FilePath m_openJDKLocation; Utils::FilePath m_keystoreLocation; diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h index 0b82feb2d48..83012d8073e 100644 --- a/src/plugins/android/androidconstants.h +++ b/src/plugins/android/androidconstants.h @@ -102,5 +102,8 @@ const Utils::Id AndroidCpuAbi = "AndroidCpuAbi"; const Utils::Id AndroidSdk = "AndroidSdk"; const Utils::Id AndroidAvdPath = "AndroidAvdPath"; +// SDK Tools +const char cmdlineToolsName[] = "cmdline-tools"; + } // namespace Constants; } // namespace Android diff --git a/src/plugins/android/androidsdkdownloader.cpp b/src/plugins/android/androidsdkdownloader.cpp index c9512b464a9..91319cb8a96 100644 --- a/src/plugins/android/androidsdkdownloader.cpp +++ b/src/plugins/android/androidsdkdownloader.cpp @@ -25,6 +25,8 @@ #include "androidsdkdownloader.h" +#include "androidconstants.h" + #include #include @@ -61,7 +63,7 @@ void AndroidSdkDownloader::sslErrors(const QList &sslErrors) } #endif -void AndroidSdkDownloader::downloadAndExtractSdk(const FilePath &sdkExtractPath) +void AndroidSdkDownloader::downloadAndExtractSdk() { if (m_androidConfig.sdkToolsUrl().isEmpty()) { logError(tr("The SDK Tools download URL is empty.")); @@ -88,11 +90,17 @@ void AndroidSdkDownloader::downloadAndExtractSdk(const FilePath &sdkExtractPath) connect(m_progressDialog, &QProgressDialog::canceled, this, &AndroidSdkDownloader::cancel); - connect(this, &AndroidSdkDownloader::sdkPackageWriteFinished, this, [this, sdkExtractPath]() { - if (Archive *archive = Archive::unarchive(m_sdkFilename, sdkExtractPath)) { - connect(archive, &Archive::finished, [this, sdkExtractPath](bool success){ - if (success) + connect(this, &AndroidSdkDownloader::sdkPackageWriteFinished, this, [this]() { + const FilePath extractDir = m_sdkFilename.parentDir(); + if (Archive *archive = Archive::unarchive(m_sdkFilename, extractDir)) { + connect(archive, &Archive::finished, [this, extractDir](bool success) { + if (success) { + // Save the extraction path temporarily which can be used by sdkmanager + // to install essential packages at firt time setup. + m_androidConfig.setTemporarySdkToolsPath( + extractDir.pathAppended(Constants::cmdlineToolsName)); emit sdkExtracted(); + } }); } }); @@ -153,7 +161,7 @@ FilePath AndroidSdkDownloader::getSaveFilename(const QUrl &url) basename += QString::number(i); } - return FilePath::fromString(QStandardPaths::writableLocation(QStandardPaths::DownloadLocation)) + return FilePath::fromString(QStandardPaths::writableLocation(QStandardPaths::TempLocation)) / basename; } diff --git a/src/plugins/android/androidsdkdownloader.h b/src/plugins/android/androidsdkdownloader.h index 76375132397..1cfe5b950dd 100644 --- a/src/plugins/android/androidsdkdownloader.h +++ b/src/plugins/android/androidsdkdownloader.h @@ -43,7 +43,7 @@ class AndroidSdkDownloader : public QObject public: AndroidSdkDownloader(); - void downloadAndExtractSdk(const Utils::FilePath &sdkExtractPath); + void downloadAndExtractSdk(); static QString dialogTitle(); void cancel(); @@ -71,7 +71,7 @@ private: QNetworkReply *m_reply = nullptr; Utils::FilePath m_sdkFilename; QProgressDialog *m_progressDialog = nullptr; - const AndroidConfig &m_androidConfig; + AndroidConfig &m_androidConfig; }; } // Internal diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp index faa4a7b089b..41dab000895 100644 --- a/src/plugins/android/androidsdkmanager.cpp +++ b/src/plugins/android/androidsdkmanager.cpp @@ -303,18 +303,18 @@ private: using MarkerTagsType = std::map; Q_GLOBAL_STATIC_WITH_ARGS(MarkerTagsType, markerTags, ({ - {SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"}, - {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"}, - {SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"}, - {SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"}, - {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"}, - {SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"}, - {SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"}, - {SdkManagerOutputParser::MarkerTag::CmdlineSdkToolsMarker, "cmdline-tools"}, - {SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"}, - {SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"}, - {SdkManagerOutputParser::MarkerTag::NdkMarker, "ndk"}, - {SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"} + {SdkManagerOutputParser::MarkerTag::InstalledPackagesMarker, "Installed packages:"}, + {SdkManagerOutputParser::MarkerTag::AvailablePackagesMarkers, "Available Packages:"}, + {SdkManagerOutputParser::MarkerTag::AvailableUpdatesMarker, "Available Updates:"}, + {SdkManagerOutputParser::MarkerTag::PlatformMarker, "platforms"}, + {SdkManagerOutputParser::MarkerTag::SystemImageMarker, "system-images"}, + {SdkManagerOutputParser::MarkerTag::BuildToolsMarker, "build-tools"}, + {SdkManagerOutputParser::MarkerTag::SdkToolsMarker, "tools"}, + {SdkManagerOutputParser::MarkerTag::CmdlineSdkToolsMarker, Constants::cmdlineToolsName}, + {SdkManagerOutputParser::MarkerTag::PlatformToolsMarker, "platform-tools"}, + {SdkManagerOutputParser::MarkerTag::EmulatorToolsMarker, "emulator"}, + {SdkManagerOutputParser::MarkerTag::NdkMarker, "ndk"}, + {SdkManagerOutputParser::MarkerTag::ExtrasMarker, "extras"} })); AndroidSdkManager::AndroidSdkManager(const AndroidConfig &config): diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp index cf920f53f13..00e597b39fb 100644 --- a/src/plugins/android/androidsettingswidget.cpp +++ b/src/plugins/android/androidsettingswidget.cpp @@ -452,6 +452,13 @@ AndroidSettingsWidget::AndroidSettingsWidget() QMessageBox::warning(this, AndroidSdkDownloader::dialogTitle(), error); }); connect(&m_sdkDownloader, &AndroidSdkDownloader::sdkExtracted, this, [this] { + // Make sure the sdk path is created before installing packages + const FilePath sdkPath = m_androidConfig.sdkLocation(); + if (!sdkPath.createDir()) { + QMessageBox::warning(this, AndroidSdkDownloader::dialogTitle(), + tr("Failed to create the SDK Tools path %1.") + .arg("\n\"" + sdkPath.toUserOutput() + "\"")); + } m_sdkManager.reloadPackages(true); updateUI(); apply(); @@ -708,7 +715,7 @@ void AndroidSettingsWidget::downloadSdk() auto userInput = QMessageBox::information(this, AndroidSdkDownloader::dialogTitle(), message, QMessageBox::Yes | QMessageBox::No); if (userInput == QMessageBox::Yes) - m_sdkDownloader.downloadAndExtractSdk(m_ui.SDKLocationPathChooser->filePath().cleanPath()); + m_sdkDownloader.downloadAndExtractSdk(); } // AndroidSettingsPage