diff options
author | Jarek Kobus <[email protected]> | 2024-10-18 14:21:25 +0200 |
---|---|---|
committer | Jarek Kobus <[email protected]> | 2024-10-31 16:26:05 +0000 |
commit | fff6188a40796caf071f7bf4827c907ef14a32fe (patch) | |
tree | 6d9925c075100a449e8a3c401fb285e10bedcefe | |
parent | 61f6539779db89a318f5a4bc24f4da441f0e08c8 (diff) |
Android: Create new AVD device asynchronously
Don't close the Create New AVD dialog when creating AVD device
failed (e.g. when other AVD with the same name already exists).
Show the cancellable progress dialog when creating an AVD.
Change-Id: I647bdcd2eada1773bbe4966f700febd26d4cd84c
Reviewed-by: Assam Boudjelthia <[email protected]>
-rw-r--r-- | src/plugins/android/androiddevice.cpp | 89 | ||||
-rw-r--r-- | src/plugins/android/androiddevice.h | 3 | ||||
-rw-r--r-- | src/plugins/android/avddialog.cpp | 86 | ||||
-rw-r--r-- | src/plugins/android/avddialog.h | 6 |
4 files changed, 118 insertions, 66 deletions
diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index ffcd74b2be1..fb614130d7f 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -975,52 +975,65 @@ void updateAvdList() s_instance->m_avdListRunner.start(s_instance->m_avdListRecipe); } -Result createAvd(const CreateAvdInfo &info, bool force) +Group createAvdRecipe(const Storage<std::optional<QString>> &errorStorage, + const CreateAvdInfo &info, bool force) { - CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"create", "avd", "-n", info.name}); - cmd.addArgs({"-k", info.sdkStylePath}); - if (info.sdcardSize > 0) - cmd.addArgs({"-c", QString("%1M").arg(info.sdcardSize)}); + struct GuardWrapper { + GuardLocker locker = GuardLocker(s_instance->m_avdPathGuard); + QByteArray buffer; + }; - const QString deviceDef = info.deviceDefinition; - if (!deviceDef.isEmpty() && deviceDef != "Custom") - cmd.addArgs({"-d", deviceDef}); + const Storage<GuardWrapper> storage; - if (force) - cmd.addArg("-f"); + const auto onSetup = [storage, info, force](Process &process) { + CommandLine cmd(AndroidConfig::avdManagerToolPath(), {"create", "avd", "-n", info.name}); + cmd.addArgs({"-k", info.sdkStylePath}); + if (info.sdcardSize > 0) + cmd.addArgs({"-c", QString("%1M").arg(info.sdcardSize)}); - Process process; - process.setProcessMode(ProcessMode::Writer); - process.setEnvironment(AndroidConfig::toolsEnvironment()); - process.setCommand(cmd); - process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile" - - QByteArray buffer; - QObject::connect(&process, &Process::readyReadStandardOutput, &process, [&process, &buffer] { - // This interaction is needed only if there is no "-d" arg for the avdmanager command. - buffer += process.readAllRawStandardOutput(); - if (buffer.endsWith(QByteArray("]:"))) { - // truncate to last line - const int index = buffer.lastIndexOf('\n'); - if (index != -1) - buffer = buffer.mid(index); - if (buffer.contains("hw.gpu.enabled")) - process.write("yes\n"); - else - process.write("\n"); - buffer.clear(); - } - }); + const QString deviceDef = info.deviceDefinition; + if (!deviceDef.isEmpty() && deviceDef != "Custom") + cmd.addArgs({"-d", deviceDef}); + + if (force) + cmd.addArg("-f"); + + process.setProcessMode(ProcessMode::Writer); + process.setEnvironment(AndroidConfig::toolsEnvironment()); + process.setCommand(cmd); + process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile" + + QByteArray *buffer = &storage->buffer; + Process *processPtr = &process; + + QObject::connect(processPtr, &Process::readyReadStandardOutput, processPtr, [processPtr, buffer] { + // This interaction is needed only if there is no "-d" arg for the avdmanager command. + *buffer += processPtr->readAllRawStandardOutput(); + if (buffer->endsWith(QByteArray("]:"))) { + // truncate to last line + const int index = buffer->lastIndexOf('\n'); + if (index != -1) + *buffer = buffer->mid(index); + if (buffer->contains("hw.gpu.enabled")) + processPtr->write("yes\n"); + else + processPtr->write("\n"); + buffer->clear(); + } + }); + }; - GuardLocker locker(s_instance->m_avdPathGuard); - process.runBlocking(); - if (process.result() != ProcessResult::FinishedWithSuccess) { + const auto onDone = [errorStorage](const Process &process) { const QString stdErr = process.stdErr(); const QString errorMessage = stdErr.isEmpty() ? process.exitMessage() : process.exitMessage() + "\n\n" + stdErr; - return Result::Error(errorMessage); - } - return Result::Ok; + *errorStorage = errorMessage; + }; + + return { + storage, + ProcessTask(onSetup, onDone, CallDoneIf::Error) + }; } // Factory diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index 7896fbc9705..c262bf1aa67 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -70,7 +70,8 @@ private: void setupDevicesWatcher(); void updateAvdList(); -Utils::Result createAvd(const CreateAvdInfo &info, bool force); +Tasking::Group createAvdRecipe(const Tasking::Storage<std::optional<QString>> &errorStorage, + const CreateAvdInfo &info, bool force); void setupAndroidDevice(); void setupAndroidDeviceManager(QObject *guard); diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index 3743715f568..052cef35368 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -26,12 +26,14 @@ #include <QLineEdit> #include <QLoggingCategory> #include <QMessageBox> +#include <QProgressDialog> #include <QPushButton> #include <QSpinBox> #include <QToolTip> #include <QSysInfo> using namespace ProjectExplorer; +using namespace Tasking; using namespace Utils; namespace Android::Internal { @@ -114,7 +116,7 @@ AvdDialog::AvdDialog(QWidget *parent) this, &AvdDialog::updateDeviceDefinitionComboBox); connect(m_abiComboBox, &QComboBox::currentIndexChanged, this, &AvdDialog::updateApiLevelComboBox); - connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(m_buttonBox, &QDialogButtonBox::accepted, this, &AvdDialog::createAvd); connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); m_deviceTypeToStringMap.insert(AvdDialog::Phone, "Phone"); @@ -131,31 +133,6 @@ AvdDialog::AvdDialog(QWidget *parent) updateApiLevelComboBox(); } -int AvdDialog::exec() -{ - const int execResult = QDialog::exec(); - if (execResult == QDialog::Accepted) { - const SystemImage *si = systemImage(); - if (!si || !si->isValid() || name().isEmpty()) { - QMessageBox::warning(Core::ICore::dialogParent(), - Tr::tr("Create new AVD"), Tr::tr("Cannot create AVD. Invalid input.")); - return QDialog::Rejected; - } - - const CreateAvdInfo avdInfo{si->sdkStylePath(), si->apiLevel(), name(), abi(), - deviceDefinition(), sdcardSize()}; - const Result result = createAvd(avdInfo, m_overwriteCheckBox->isChecked()); - if (!result) { - QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Create new AVD"), - result.error()); - return QDialog::Rejected; - } - m_createdAvdInfo = avdInfo; - updateAvdList(); - } - return execResult; -} - bool AvdDialog::isValid() const { return !name().isEmpty() && systemImage() && systemImage()->isValid() && !abi().isEmpty(); @@ -179,6 +156,63 @@ AvdDialog::DeviceType AvdDialog::tagToDeviceType(const QString &type_tag) return AvdDialog::PhoneOrTablet; } +void AvdDialog::createAvd() +{ + const SystemImage *si = systemImage(); + if (!si || !si->isValid() || name().isEmpty()) { + QMessageBox::warning(Core::ICore::dialogParent(), + Tr::tr("Create new AVD"), Tr::tr("Cannot create AVD. Invalid input.")); + return; + } + const CreateAvdInfo avdInfo{si->sdkStylePath(), si->apiLevel(), name(), abi(), + deviceDefinition(), sdcardSize()}; + + struct Progress { + Progress() { + progressDialog.reset(new QProgressDialog(Core::ICore::dialogParent())); + progressDialog->setRange(0, 0); + progressDialog->setWindowModality(Qt::ApplicationModal); + progressDialog->setWindowTitle("Create new AVD"); + progressDialog->setLabelText(Tr::tr("Creating new AVD device...")); + progressDialog->setFixedSize(progressDialog->sizeHint()); + progressDialog->setAutoClose(false); + progressDialog->show(); // TODO: Should not be needed. Investigate possible QT_BUG + } + std::unique_ptr<QProgressDialog> progressDialog; + }; + + const Storage<Progress> progressStorage; + + const auto onCancelSetup = [progressStorage] { + return std::make_pair(progressStorage->progressDialog.get(), &QProgressDialog::canceled); + }; + + const Storage<std::optional<QString>> errorStorage; + + const auto onDone = [errorStorage] { + if (errorStorage->has_value()) { + QMessageBox::warning(Core::ICore::dialogParent(), Tr::tr("Create new AVD"), + errorStorage->value()); + } + }; + + const Group recipe { + progressStorage, + errorStorage, + createAvdRecipe(errorStorage, avdInfo, m_overwriteCheckBox->isChecked()) + .withCancel(onCancelSetup), + onGroupDone(onDone, CallDoneIf::Error) + }; + + m_taskTreeRunner.start(recipe, {}, [this, avdInfo](DoneWith result) { + if (result == DoneWith::Error) + return; + m_createdAvdInfo = avdInfo; + updateAvdList(); + accept(); + }); +} + static bool avdManagerCommand(const QStringList &args, QString *output) { CommandLine cmd(AndroidConfig::avdManagerToolPath(), args); diff --git a/src/plugins/android/avddialog.h b/src/plugins/android/avddialog.h index 550a04a158e..efbabf81c53 100644 --- a/src/plugins/android/avddialog.h +++ b/src/plugins/android/avddialog.h @@ -5,6 +5,8 @@ #include "androidconfigurations.h" +#include <solutions/tasking/tasktreerunner.h> + #include <QDialog> #include <QRegularExpression> #include <QTimer> @@ -30,7 +32,6 @@ class AvdDialog : public QDialog { public: explicit AvdDialog(QWidget *parent = nullptr); - int exec() override; CreateAvdInfo avdInfo() const; private: @@ -50,6 +51,8 @@ private: static AvdDialog::DeviceType tagToDeviceType(const QString &type_tag); + void createAvd(); + struct DeviceDefinitionStruct { QString name_id; @@ -72,6 +75,7 @@ private: QComboBox *m_deviceDefinitionTypeComboBox; QCheckBox *m_overwriteCheckBox; QDialogButtonBox *m_buttonBox; + Tasking::TaskTreeRunner m_taskTreeRunner; }; } // Internal |