Android: Collect initial data inside AvdDialog asynchronously

Disable all GUI controls but cancel button and execute the list
command asynchronously. Show a spinner during process execution.
Re-enable GUI controls on successful execution.
Show message box on failure and reject the AvdDialog.

Change-Id: Idf3c5fc2f6d16f752999368bdecab24edfc51915
Reviewed-by: Assam Boudjelthia <assam.boudjelthia@qt.io>
This commit is contained in:
Jarek Kobus
2024-10-18 14:21:25 +02:00
parent fff6188a40
commit db5a39311f
2 changed files with 111 additions and 89 deletions

View File

@@ -11,6 +11,8 @@
#include <projectexplorer/projectexplorerconstants.h>
#include <solutions/spinner/spinner.h>
#include <utils/algorithm.h>
#include <utils/infolabel.h>
#include <utils/layoutbuilder.h>
@@ -33,6 +35,7 @@
#include <QSysInfo>
using namespace ProjectExplorer;
using namespace SpinnerSolution;
using namespace Tasking;
using namespace Utils;
@@ -96,8 +99,10 @@ AvdDialog::AvdDialog(QWidget *parent)
using namespace Layouting;
m_gui = new QWidget;
Column {
Form {
m_gui = Form {
Tr::tr("Name:"), m_nameLineEdit, br,
Tr::tr("Target ABI / API:"),
Row { m_abiComboBox, m_targetApiComboBox }, br,
@@ -106,7 +111,8 @@ AvdDialog::AvdDialog(QWidget *parent)
Row { m_deviceDefinitionTypeComboBox, m_deviceDefinitionComboBox }, br,
Tr::tr("SD card size:"), m_sdcardSizeSpinBox, br,
QString(), m_overwriteCheckBox,
},
noMargin
}.emerge(),
st,
m_buttonBox
}.attachTo(this);
@@ -126,11 +132,7 @@ AvdDialog::AvdDialog(QWidget *parent)
m_deviceTypeToStringMap.insert(AvdDialog::Wear, "Wear");
m_deviceTypeToStringMap.insert(AvdDialog::Desktop, "Desktop");
parseDeviceDefinitionsList();
for (const QString &type : m_deviceTypeToStringMap)
m_deviceDefinitionTypeComboBox->addItem(type);
updateApiLevelComboBox();
collectInitialData();
}
bool AvdDialog::isValid() const
@@ -156,6 +158,106 @@ AvdDialog::DeviceType AvdDialog::tagToDeviceType(const QString &type_tag)
return AvdDialog::PhoneOrTablet;
}
void AvdDialog::collectInitialData()
{
const auto onProcessSetup = [this](Process &process) {
m_gui->setEnabled(false);
m_buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
const CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"list", "device"});
qCDebug(avdDialogLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput();
process.setEnvironment(AndroidConfig::toolsEnvironment());
process.setCommand(cmd);
};
const auto onProcessDone = [this](const Process &process, DoneWith result) {
const QString output = process.allOutput();
if (result == DoneWith::Error) {
QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Create new AVD"),
Tr::tr("Avd list command failed. %1 %2")
.arg(output).arg(AndroidConfig::sdkToolsVersion().toString()));
reject();
return;
}
/* Example output:
Available devices definitions:
id: 0 or "automotive_1024p_landscape"
Name: Automotive (1024p landscape)
OEM : Google
Tag : android-automotive-playstore
---------
id: 1 or "automotive_1080p_landscape"
Name: Automotive (1080p landscape)
OEM : Google
Tag : android-automotive
---------
id: 2 or "Galaxy Nexus"
Name: Galaxy Nexus
OEM : Google
---------
id: 3 or "desktop_large"
Name: Large Desktop
OEM : Google
Tag : android-desktop
...
*/
QStringList avdDeviceInfo;
const auto lines = output.split('\n');
for (const QString &line : lines) {
if (line.startsWith("---------") || line.isEmpty()) {
DeviceDefinitionStruct deviceDefinition;
for (const QString &line : avdDeviceInfo) {
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;
}
}
for (const QString &type : m_deviceTypeToStringMap)
m_deviceDefinitionTypeComboBox->addItem(type);
updateApiLevelComboBox();
m_gui->setEnabled(true);
};
struct SpinnerStruct {
std::unique_ptr<Spinner> spinner;
};
const Storage<SpinnerStruct> storage;
const auto onSetup = [this, storage] {
storage->spinner.reset(new Spinner(SpinnerSize::Medium, m_gui));
storage->spinner->show();
};
const Group recipe {
storage,
onGroupSetup(onSetup),
ProcessTask(onProcessSetup, onProcessDone)
};
m_taskTreeRunner.start(recipe);
}
void AvdDialog::createAvd()
{
const SystemImage *si = systemImage();
@@ -213,87 +315,6 @@ void AvdDialog::createAvd()
});
}
static bool avdManagerCommand(const QStringList &args, QString *output)
{
CommandLine cmd(AndroidConfig::avdManagerToolPath(), args);
Process proc;
proc.setEnvironment(AndroidConfig::toolsEnvironment());
qCDebug(avdDialogLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput();
proc.setCommand(cmd);
proc.runBlocking();
if (proc.result() == ProcessResult::FinishedWithSuccess) {
if (output)
*output = proc.allOutput();
return true;
}
return false;
}
void AvdDialog::parseDeviceDefinitionsList()
{
QString output;
if (!avdManagerCommand({"list", "device"}, &output)) {
qCDebug(avdDialogLog) << "Avd list command failed" << output
<< AndroidConfig::sdkToolsVersion();
return;
}
/* Example output:
Available devices definitions:
id: 0 or "automotive_1024p_landscape"
Name: Automotive (1024p landscape)
OEM : Google
Tag : android-automotive-playstore
---------
id: 1 or "automotive_1080p_landscape"
Name: Automotive (1080p landscape)
OEM : Google
Tag : android-automotive
---------
id: 2 or "Galaxy Nexus"
Name: Galaxy Nexus
OEM : Google
---------
id: 3 or "desktop_large"
Name: Large Desktop
OEM : Google
Tag : android-desktop
...
*/
QStringList avdDeviceInfo;
const auto lines = output.split('\n');
for (const QString &line : lines) {
if (line.startsWith("---------") || line.isEmpty()) {
DeviceDefinitionStruct deviceDefinition;
for (const QString &line : avdDeviceInfo) {
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 = m_deviceTypeToStringMap.key(

View File

@@ -44,13 +44,13 @@ private:
int sdcardSize() const;
bool isValid() const;
void parseDeviceDefinitionsList();
void updateDeviceDefinitionComboBox();
void updateApiLevelComboBox();
bool eventFilter(QObject *obj, QEvent *event) override;
static AvdDialog::DeviceType tagToDeviceType(const QString &type_tag);
void collectInitialData();
void createAvd();
struct DeviceDefinitionStruct
@@ -66,6 +66,7 @@ private:
QList<DeviceDefinitionStruct> m_deviceDefinitionsList;
QMap<AvdDialog::DeviceType, QString> m_deviceTypeToStringMap;
QWidget *m_gui;
QComboBox *m_abiComboBox;
QSpinBox *m_sdcardSizeSpinBox;
QLineEdit *m_nameLineEdit;