diff --git a/src/plugins/android/addnewavddialog.ui b/src/plugins/android/addnewavddialog.ui
index 71abe36e9be..c60b43fdb3e 100644
--- a/src/plugins/android/addnewavddialog.ui
+++ b/src/plugins/android/addnewavddialog.ui
@@ -7,7 +7,7 @@
0
0
600
- 187
+ 243
@@ -30,45 +30,49 @@
-
+
+
+ 0
+ 0
+
+
Name:
- -
-
-
- -
+
-
Architecture (ABI):
- -
-
-
- -
-
-
- Target API:
-
-
-
- -
-
-
- -
-
-
- -
+
-
SD card size:
- -
+
-
+
+
+ Target API:
+
+
+
+ -
+
+
+ Device definition:
+
+
+
+ -
+
+
+ -
Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter
@@ -87,6 +91,35 @@
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+ -
+
+
+
+ 0
+ 0
+
+
+
+
+ -
+
+
+ Overwrite existing AVD name
+
+
+
-
diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp
index 10ec89118a6..9d871a6fff8 100644
--- a/src/plugins/android/androidavdmanager.cpp
+++ b/src/plugins/android/androidavdmanager.cpp
@@ -71,7 +71,7 @@ const int avdCreateTimeoutMs = 30000;
\c true if the command is successfully executed. Output is copied into \a output. The function
blocks the calling thread.
*/
-static bool avdManagerCommand(const AndroidConfig config, const QStringList &args, QString *output)
+bool AndroidAvdManager::avdManagerCommand(const AndroidConfig config, const QStringList &args, QString *output)
{
CommandLine cmd(config.avdManagerToolPath(), args);
Utils::SynchronousProcess proc;
@@ -118,7 +118,7 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig config, const CreateAv
if (!result.isValid()) {
qCDebug(avdManagerLog) << "AVD Create failed. Invalid CreateAvdInfo" << result.name
- << result.sdkPlatform->displayText() << result.sdkPlatform->apiLevel();
+ << result.systemImage->displayText() << result.systemImage->apiLevel();
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Invalid input.");
return result;
@@ -126,28 +126,17 @@ static CreateAvdInfo createAvdCommand(const AndroidConfig config, const CreateAv
QStringList arguments({"create", "avd", "-n", result.name});
- if (!result.abi.isEmpty()) {
- SystemImage *image = Utils::findOrDefault(result.sdkPlatform->systemImages(),
- Utils::equal(&SystemImage::abiName, result.abi));
- 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 << name;
- result.error = QApplication::translate("AndroidAvdManager",
- "Cannot create AVD. Cannot find system image for "
- "the ABI %1(%2).").arg(result.abi).arg(name);
- return result;
- }
-
- } else {
- arguments << "-k" << result.sdkPlatform->sdkStylePath();
- }
+ arguments << "-k" << result.systemImage->sdkStylePath();
if (result.sdcardSize > 0)
arguments << "-c" << QString::fromLatin1("%1M").arg(result.sdcardSize);
+ if (!result.deviceDefinition.isEmpty() && result.deviceDefinition != "Custom")
+ arguments << "-d" << QString::fromLatin1("%1").arg(result.deviceDefinition);
+
+ if (result.overwrite)
+ arguments << "-f";
+
QProcess proc;
proc.start(config.avdManagerToolPath().toString(), arguments);
if (!proc.waitForStarted()) {
@@ -419,7 +408,7 @@ AndroidDeviceInfoList AvdManagerOutputParser::listVirtualDevices(const AndroidCo
AndroidDeviceInfoList avdList;
do {
- if (!avdManagerCommand(config, {"list", "avd"}, &output)) {
+ if (!AndroidAvdManager::avdManagerCommand(config, {"list", "avd"}, &output)) {
qCDebug(avdManagerLog)
<< "Avd list command failed" << output << config.sdkToolsVersion();
return {};
diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h
index 777fa5a9f8b..41244b32724 100644
--- a/src/plugins/android/androidavdmanager.h
+++ b/src/plugins/android/androidavdmanager.h
@@ -54,6 +54,9 @@ public:
QString waitForAvd(const QString &avdName,
const std::function &cancelChecker = {}) const;
bool isAvdBooted(const QString &device) const;
+ static bool avdManagerCommand(const AndroidConfig config,
+ const QStringList &args,
+ QString *output);
private:
bool waitForBooted(const QString &serialNumber,
diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h
index 55a72747e2f..1756056b25d 100644
--- a/src/plugins/android/androidconfigurations.h
+++ b/src/plugins/android/androidconfigurations.h
@@ -85,12 +85,14 @@ using AndroidDeviceInfoList = QList;
class CreateAvdInfo
{
public:
- bool isValid() const { return sdkPlatform && sdkPlatform->isValid() && !name.isEmpty(); }
- const SdkPlatform *sdkPlatform = nullptr;
+ bool isValid() const { return systemImage && systemImage->isValid() && !name.isEmpty(); }
+ const SystemImage *systemImage = nullptr;
QString name;
QString abi;
+ QString deviceDefinition;
int sdcardSize = 0;
QString error; // only used in the return value of createAVD
+ bool overwrite = false;
};
class ANDROID_EXPORT AndroidConfig
diff --git a/src/plugins/android/androiddevicedialog.cpp b/src/plugins/android/androiddevicedialog.cpp
index cd8ae2f6114..4fb41262126 100644
--- a/src/plugins/android/androiddevicedialog.cpp
+++ b/src/plugins/android/androiddevicedialog.cpp
@@ -578,7 +578,7 @@ void AndroidDeviceDialog::createAvd()
{
m_ui->createAVDButton->setEnabled(false);
CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, AndroidConfigurations::sdkManager(),
- m_apiLevel, m_abis);
+ AndroidConfigurations::currentConfig(), m_apiLevel, m_abis);
if (!info.isValid()) {
m_ui->createAVDButton->setEnabled(true);
diff --git a/src/plugins/android/androidsdkmanager.cpp b/src/plugins/android/androidsdkmanager.cpp
index b77bdd679ae..6dc76a5f780 100644
--- a/src/plugins/android/androidsdkmanager.cpp
+++ b/src/plugins/android/androidsdkmanager.cpp
@@ -345,6 +345,21 @@ AndroidSdkPackageList AndroidSdkManager::installedSdkPackages()
return m_d->filteredPackages(AndroidSdkPackage::Installed, AndroidSdkPackage::AnyValidType);
}
+SystemImageList AndroidSdkManager::installedSystemImages()
+{
+ AndroidSdkPackageList list = m_d->filteredPackages(AndroidSdkPackage::AnyValidState,
+ AndroidSdkPackage::SdkPlatformPackage);
+ QList platforms = Utils::static_container_cast(list);
+
+ SystemImageList result;
+ for (SdkPlatform *platform : platforms) {
+ if (platform->systemImages().size() > 0)
+ result.append(platform->systemImages());
+ }
+
+ return result;
+}
+
SdkPlatform *AndroidSdkManager::latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state)
{
SdkPlatform *result = nullptr;
@@ -679,6 +694,7 @@ QPair SdkManagerOutputParser::parseSystemImage(const QString
image->setInstalledLocation(packageData.installedLocation);
image->setDisplayText(packageData.description);
image->setDescriptionText(packageData.description);
+ image->setApiLevel(apiLevel);
result = qMakePair(image, apiLevel);
} else {
qCDebug(sdkManagerLog) << "System-image: Minimum required data unavailable: "<< data;
diff --git a/src/plugins/android/androidsdkmanager.h b/src/plugins/android/androidsdkmanager.h
index a78a9fbaab9..fa0d52e1acc 100644
--- a/src/plugins/android/androidsdkmanager.h
+++ b/src/plugins/android/androidsdkmanager.h
@@ -69,6 +69,7 @@ public:
const AndroidSdkPackageList &allSdkPackages();
AndroidSdkPackageList availableSdkPackages();
AndroidSdkPackageList installedSdkPackages();
+ SystemImageList installedSystemImages();
SdkPlatform *latestAndroidSdkPlatform(AndroidSdkPackage::PackageState state
= AndroidSdkPackage::Installed);
diff --git a/src/plugins/android/androidsdkpackage.cpp b/src/plugins/android/androidsdkpackage.cpp
index 59978b3b7d6..8c6e0d79114 100644
--- a/src/plugins/android/androidsdkpackage.cpp
+++ b/src/plugins/android/androidsdkpackage.cpp
@@ -134,6 +134,16 @@ void SystemImage::setPlatform(SdkPlatform *platform)
m_platform = platform;
}
+int SystemImage::apiLevel() const
+{
+ return m_apiLevel;
+}
+
+void SystemImage::setApiLevel(const int apiLevel)
+{
+ m_apiLevel = apiLevel;
+}
+
SdkPlatform::SdkPlatform(QVersionNumber version, QString sdkStylePathStr, int api, QObject *parent) :
AndroidSdkPackage(version, sdkStylePathStr, parent),
m_apiLevel(api)
diff --git a/src/plugins/android/androidsdkpackage.h b/src/plugins/android/androidsdkpackage.h
index 9f85e87be9c..035e146f97b 100644
--- a/src/plugins/android/androidsdkpackage.h
+++ b/src/plugins/android/androidsdkpackage.h
@@ -114,10 +114,13 @@ public:
const QString &abiName() const;
const SdkPlatform *platform() const;
void setPlatform(SdkPlatform *platform);
+ int apiLevel() const;
+ void setApiLevel(const int apiLevel);
private:
QPointer m_platform;
QString m_abiName;
+ int m_apiLevel = -1;
};
using SystemImageList = QList;
diff --git a/src/plugins/android/androidsettingswidget.cpp b/src/plugins/android/androidsettingswidget.cpp
index 0e9a97e98a4..65015240f0e 100644
--- a/src/plugins/android/androidsettingswidget.cpp
+++ b/src/plugins/android/androidsettingswidget.cpp
@@ -260,7 +260,8 @@ QVariant AvdModel::data(const QModelIndex &index, int role) const
return cpuAbis.isEmpty() ? QVariant() : QVariant(cpuAbis.first());
}
case 3:
- return currentRow.avdDevice;
+ return currentRow.avdDevice.isEmpty() ? QVariant("Custom")
+ : currentRow.avdDevice;
case 4:
return currentRow.avdTarget;
case 5:
@@ -556,7 +557,7 @@ void AndroidSettingsWidget::openOpenJDKDownloadUrl()
void AndroidSettingsWidget::addAVD()
{
disableAvdControls();
- CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, m_sdkManager.get());
+ CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, m_sdkManager.get(), m_androidConfig);
if (!info.isValid()) {
enableAvdControls();
diff --git a/src/plugins/android/androidtoolmanager.cpp b/src/plugins/android/androidtoolmanager.cpp
index 2db6cecb265..b52a2916df1 100644
--- a/src/plugins/android/androidtoolmanager.cpp
+++ b/src/plugins/android/androidtoolmanager.cpp
@@ -148,7 +148,7 @@ CreateAvdInfo AndroidToolManager::createAvdImpl(CreateAvdInfo info, FilePath and
proc.setProcessEnvironment(env);
QStringList arguments;
arguments << QLatin1String("create") << QLatin1String("avd")
- << QLatin1String("-t") << AndroidConfig::apiLevelNameFor(info.sdkPlatform)
+ << QLatin1String("-t") << QString("android-%1").arg(info.systemImage->apiLevel())
<< QLatin1String("-n") << info.name
<< QLatin1String("-b") << info.abi;
if (info.sdcardSize > 0)
diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp
index aa1b0422f89..0c2ce72acc5 100644
--- a/src/plugins/android/avddialog.cpp
+++ b/src/plugins/android/avddialog.cpp
@@ -25,6 +25,7 @@
#include "avddialog.h"
#include "androidsdkmanager.h"
+#include "androidavdmanager.h"
#include
#include
@@ -34,16 +35,22 @@
#include
#include
#include
+#include
using namespace Android;
using namespace Android::Internal;
+namespace {
+Q_LOGGING_CATEGORY(avdDialogLog, "qtc.android.avdDialog", QtWarningMsg)
+}
+
AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QStringList &abis,
- QWidget *parent) :
+ const AndroidConfig &config, QWidget *parent) :
QDialog(parent),
m_sdkManager(sdkManager),
m_minApiLevel(minApiLevel),
- m_allowedNameChars(QLatin1String("[a-z|A-Z|0-9|._-]*"))
+ m_allowedNameChars(QLatin1String("[a-z|A-Z|0-9|._-]*")),
+ m_androidConfig(config)
{
QTC_CHECK(m_sdkManager);
m_avdDialog.setupUi(this);
@@ -63,39 +70,119 @@ AvdDialog::AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QStri
m_avdDialog.warningText->setType(Utils::InfoLabel::Warning);
- updateApiLevelComboBox();
+ connect(&m_hideTipTimer, &QTimer::timeout, this, []() { Utils::ToolTip::hide(); });
+ parseDeviceDefinitionsList();
+ for (const QString &type : DeviceTypeToStringMap.values())
+ m_avdDialog.deviceDefinitionTypeComboBox->addItem(type);
+
+ connect(m_avdDialog.deviceDefinitionTypeComboBox,
+ QOverload::of(&QComboBox::currentIndexChanged),
+ this,
+ &AvdDialog::updateDeviceDefinitionComboBox);
connect(m_avdDialog.abiComboBox,
QOverload::of(&QComboBox::currentIndexChanged),
this, &AvdDialog::updateApiLevelComboBox);
- connect(&m_hideTipTimer, &QTimer::timeout,
- this, [](){Utils::ToolTip::hide();});
+ m_avdDialog.deviceDefinitionTypeComboBox->setCurrentIndex(1); // Set Phone type as default index
+ updateApiLevelComboBox();
}
bool AvdDialog::isValid() const
{
- return !name().isEmpty() && sdkPlatform() && sdkPlatform()->isValid() && !abi().isEmpty();
+ return !name().isEmpty() && systemImage() && systemImage()->isValid() && !abi().isEmpty();
}
CreateAvdInfo AvdDialog::gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager,
- int minApiLevel, const QStringList &abis)
+ const AndroidConfig &config, int minApiLevel, const QStringList &abis)
{
CreateAvdInfo result;
- AvdDialog d(minApiLevel, sdkManager, abis, parent);
+ AvdDialog d(minApiLevel, sdkManager, abis, config, parent);
if (d.exec() != QDialog::Accepted || !d.isValid())
return result;
- result.sdkPlatform = d.sdkPlatform();
+ result.systemImage = d.systemImage();
result.name = d.name();
result.abi = d.abi();
+ result.deviceDefinition = d.deviceDefinition();
result.sdcardSize = d.sdcardSize();
+ result.overwrite = d.m_avdDialog.overwriteCheckBox->isChecked();
+
return result;
}
-const SdkPlatform* AvdDialog::sdkPlatform() const
+AvdDialog::DeviceType AvdDialog::tagToDeviceType(const QString &type_tag)
{
- return m_avdDialog.targetApiComboBox->currentData().value();
+ if (type_tag.contains("android-wear"))
+ return AvdDialog::Wear;
+ else if (type_tag.contains("android-tv"))
+ return AvdDialog::TV;
+ else if (type_tag.contains("android-automotive"))
+ return AvdDialog::Automotive;
+ else
+ return AvdDialog::PhoneOrTablet;
+}
+
+void AvdDialog::parseDeviceDefinitionsList()
+{
+ QString output;
+
+ if (!AndroidAvdManager::avdManagerCommand(m_androidConfig, {"list", "device"}, &output)) {
+ qCDebug(avdDialogLog) << "Avd list command failed" << output
+ << m_androidConfig.sdkToolsVersion();
+ return;
+ }
+
+ QStringList avdDeviceInfo;
+
+ for (QString line : output.split('\n')) {
+ if (line.startsWith("---------") || line.isEmpty()) {
+ DeviceDefinitionStruct deviceDefinition;
+ for (const QString &line : avdDeviceInfo) {
+ QString value;
+ if (line.contains("id:")) {
+ deviceDefinition.name_id = line.split("or").at(1);
+ deviceDefinition.name_id = deviceDefinition.name_id.remove(0, 1).remove('"');
+ } else if (line.contains("Tag :")) {
+ deviceDefinition.type_str = line.split(':').at(1);
+ deviceDefinition.type_str = deviceDefinition.type_str.remove(0, 1);
+ }
+ }
+
+ DeviceType deviceType = tagToDeviceType(deviceDefinition.type_str);
+ if (deviceType == PhoneOrTablet) {
+ if (deviceDefinition.name_id.contains("Tablet"))
+ deviceType = Tablet;
+ else
+ deviceType = Phone;
+ }
+ deviceDefinition.deviceType = deviceType;
+ m_deviceDefinitionsList.append(deviceDefinition);
+ avdDeviceInfo.clear();
+ } else {
+ avdDeviceInfo << line;
+ }
+ }
+}
+
+void AvdDialog::updateDeviceDefinitionComboBox()
+{
+ DeviceType curDeviceType = DeviceTypeToStringMap.key(
+ m_avdDialog.deviceDefinitionTypeComboBox->currentText());
+
+ m_avdDialog.deviceDefinitionComboBox->clear();
+ for (auto item : m_deviceDefinitionsList) {
+ if (item.deviceType == curDeviceType)
+ m_avdDialog.deviceDefinitionComboBox->addItem(item.name_id);
+ }
+ m_avdDialog.deviceDefinitionComboBox->addItem("Custom");
+
+ updateApiLevelComboBox();
+}
+
+const SystemImage* AvdDialog::systemImage() const
+{
+ return m_avdDialog.targetApiComboBox->currentData().value();
}
QString AvdDialog::name() const
@@ -108,6 +195,11 @@ QString AvdDialog::abi() const
return m_avdDialog.abiComboBox->currentText();
}
+QString AvdDialog::deviceDefinition() const
+{
+ return m_avdDialog.deviceDefinitionComboBox->currentText();
+}
+
int AvdDialog::sdcardSize() const
{
return m_avdDialog.sdcardSizeSpinBox->value();
@@ -115,37 +207,49 @@ int AvdDialog::sdcardSize() const
void AvdDialog::updateApiLevelComboBox()
{
- SdkPlatformList filteredList;
- const SdkPlatformList platforms = m_sdkManager->filteredSdkPlatforms(m_minApiLevel);
+ SystemImageList installedSystemImages = m_sdkManager->installedSystemImages();
+ DeviceType curDeviceType = DeviceTypeToStringMap.key(
+ m_avdDialog.deviceDefinitionTypeComboBox->currentText());
QString selectedAbi = abi();
auto hasAbi = [selectedAbi](const SystemImage *image) {
return image && image->isValid() && (image->abiName() == selectedAbi);
};
- filteredList = Utils::filtered(platforms, [hasAbi](const SdkPlatform *platform) {
- return platform && Utils::anyOf(platform->systemImages(), hasAbi);
+ SystemImageList filteredList;
+ filteredList = Utils::filtered(installedSystemImages, [hasAbi, &curDeviceType](const SystemImage *image) {
+ DeviceType deviceType = tagToDeviceType(image->sdkStylePath().split(';').at(2));
+ if (deviceType == PhoneOrTablet && (curDeviceType == Phone || curDeviceType == Tablet))
+ curDeviceType = PhoneOrTablet;
+ return image && deviceType == curDeviceType && hasAbi(image);
});
m_avdDialog.targetApiComboBox->clear();
- for (SdkPlatform *platform: filteredList) {
- m_avdDialog.targetApiComboBox->addItem(platform->displayText(),
- QVariant::fromValue(platform));
- m_avdDialog.targetApiComboBox->setItemData(m_avdDialog.targetApiComboBox->count() - 1,
- platform->descriptionText(), Qt::ToolTipRole);
+ for (SystemImage *image : filteredList) {
+ QString imageString = "android-" % QString::number(image->apiLevel());
+ if (image->sdkStylePath().contains("playstore"))
+ imageString += " (Google PlayStore)";
+ m_avdDialog.targetApiComboBox->addItem(imageString,
+ QVariant::fromValue(image));
+ m_avdDialog.targetApiComboBox->setItemData(m_avdDialog.targetApiComboBox->count() - 1,
+ image->descriptionText(),
+ Qt::ToolTipRole);
}
- if (platforms.isEmpty()) {
+ if (installedSystemImages.isEmpty()) {
+ m_avdDialog.targetApiComboBox->setEnabled(false);
m_avdDialog.warningText->setVisible(true);
m_avdDialog.warningText->setText(tr("Cannot create a new AVD. No sufficiently recent Android SDK available.\n"
"Install an SDK of at least API version %1.")
.arg(m_minApiLevel));
} else if (filteredList.isEmpty()) {
+ m_avdDialog.targetApiComboBox->setEnabled(false);
m_avdDialog.warningText->setVisible(true);
m_avdDialog.warningText->setText(tr("Cannot create a AVD for ABI %1. Install an image for it.")
.arg(abi()));
} else {
m_avdDialog.warningText->setVisible(false);
+ m_avdDialog.targetApiComboBox->setEnabled(true);
}
}
diff --git a/src/plugins/android/avddialog.h b/src/plugins/android/avddialog.h
index 6f32cd43a6e..ba3d75837cd 100644
--- a/src/plugins/android/avddialog.h
+++ b/src/plugins/android/avddialog.h
@@ -26,6 +26,7 @@
#pragma once
#include "androidconfigurations.h"
#include "ui_addnewavddialog.h"
+#include "androidconfigurations.h"
#include
#include
@@ -40,18 +41,43 @@ class AvdDialog : public QDialog
{
Q_OBJECT
public:
- explicit AvdDialog(int minApiLevel, AndroidSdkManager *sdkManager, const QStringList &abis,
+ explicit AvdDialog(int minApiLevel,
+ AndroidSdkManager *sdkManager,
+ const QStringList &abis,
+ const AndroidConfig &config,
QWidget *parent = nullptr);
- const SdkPlatform *sdkPlatform() const;
+ enum DeviceType { TV, Phone, Wear, Tablet, Automotive, PhoneOrTablet };
+
+ const QMap DeviceTypeToStringMap{
+ {TV, "TV"},
+ {Phone, "Phone"},
+ {Wear, "Wear"},
+ {Tablet, "Tablet"},
+ {Automotive, "Automotive"}
+ };
+
+ struct DeviceDefinitionStruct
+ {
+ QString name_id;
+ QString type_str;
+ DeviceType deviceType;
+ };
+
+ const SystemImage *systemImage() const;
QString name() const;
QString abi() const;
+ QString deviceDefinition() const;
int sdcardSize() const;
bool isValid() const;
+ static AvdDialog::DeviceType tagToDeviceType(const QString &type_tag);
static CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, AndroidSdkManager *sdkManager,
+ const AndroidConfig &config,
int minApiLevel = 0, const QStringList &abis = {});
private:
+ void parseDeviceDefinitionsList();
+ void updateDeviceDefinitionComboBox();
void updateApiLevelComboBox();
bool eventFilter(QObject *obj, QEvent *event) override;
@@ -60,6 +86,8 @@ private:
int m_minApiLevel;
QTimer m_hideTipTimer;
QRegExp m_allowedNameChars;
+ QList m_deviceDefinitionsList;
+ AndroidConfig m_androidConfig;
};
}
}