forked from qt-creator/qt-creator
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 <assam.boudjelthia@qt.io>
This commit is contained in:
@@ -975,52 +975,65 @@ void updateAvdList()
|
|||||||
s_instance->m_avdListRunner.start(s_instance->m_avdListRecipe);
|
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});
|
struct GuardWrapper {
|
||||||
cmd.addArgs({"-k", info.sdkStylePath});
|
GuardLocker locker = GuardLocker(s_instance->m_avdPathGuard);
|
||||||
if (info.sdcardSize > 0)
|
QByteArray buffer;
|
||||||
cmd.addArgs({"-c", QString("%1M").arg(info.sdcardSize)});
|
};
|
||||||
|
|
||||||
const QString deviceDef = info.deviceDefinition;
|
const Storage<GuardWrapper> storage;
|
||||||
if (!deviceDef.isEmpty() && deviceDef != "Custom")
|
|
||||||
cmd.addArgs({"-d", deviceDef});
|
|
||||||
|
|
||||||
if (force)
|
const auto onSetup = [storage, info, force](Process &process) {
|
||||||
cmd.addArg("-f");
|
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;
|
const QString deviceDef = info.deviceDefinition;
|
||||||
process.setProcessMode(ProcessMode::Writer);
|
if (!deviceDef.isEmpty() && deviceDef != "Custom")
|
||||||
process.setEnvironment(AndroidConfig::toolsEnvironment());
|
cmd.addArgs({"-d", deviceDef});
|
||||||
process.setCommand(cmd);
|
|
||||||
process.setWriteData("yes\n"); // yes to "Do you wish to create a custom hardware profile"
|
|
||||||
|
|
||||||
QByteArray buffer;
|
if (force)
|
||||||
QObject::connect(&process, &Process::readyReadStandardOutput, &process, [&process, &buffer] {
|
cmd.addArg("-f");
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
GuardLocker locker(s_instance->m_avdPathGuard);
|
process.setProcessMode(ProcessMode::Writer);
|
||||||
process.runBlocking();
|
process.setEnvironment(AndroidConfig::toolsEnvironment());
|
||||||
if (process.result() != ProcessResult::FinishedWithSuccess) {
|
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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto onDone = [errorStorage](const Process &process) {
|
||||||
const QString stdErr = process.stdErr();
|
const QString stdErr = process.stdErr();
|
||||||
const QString errorMessage = stdErr.isEmpty() ? process.exitMessage()
|
const QString errorMessage = stdErr.isEmpty() ? process.exitMessage()
|
||||||
: process.exitMessage() + "\n\n" + stdErr;
|
: process.exitMessage() + "\n\n" + stdErr;
|
||||||
return Result::Error(errorMessage);
|
*errorStorage = errorMessage;
|
||||||
}
|
};
|
||||||
return Result::Ok;
|
|
||||||
|
return {
|
||||||
|
storage,
|
||||||
|
ProcessTask(onSetup, onDone, CallDoneIf::Error)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Factory
|
// Factory
|
||||||
|
@@ -70,7 +70,8 @@ private:
|
|||||||
|
|
||||||
void setupDevicesWatcher();
|
void setupDevicesWatcher();
|
||||||
void updateAvdList();
|
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 setupAndroidDevice();
|
||||||
void setupAndroidDeviceManager(QObject *guard);
|
void setupAndroidDeviceManager(QObject *guard);
|
||||||
|
@@ -26,12 +26,14 @@
|
|||||||
#include <QLineEdit>
|
#include <QLineEdit>
|
||||||
#include <QLoggingCategory>
|
#include <QLoggingCategory>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QProgressDialog>
|
||||||
#include <QPushButton>
|
#include <QPushButton>
|
||||||
#include <QSpinBox>
|
#include <QSpinBox>
|
||||||
#include <QToolTip>
|
#include <QToolTip>
|
||||||
#include <QSysInfo>
|
#include <QSysInfo>
|
||||||
|
|
||||||
using namespace ProjectExplorer;
|
using namespace ProjectExplorer;
|
||||||
|
using namespace Tasking;
|
||||||
using namespace Utils;
|
using namespace Utils;
|
||||||
|
|
||||||
namespace Android::Internal {
|
namespace Android::Internal {
|
||||||
@@ -114,7 +116,7 @@ AvdDialog::AvdDialog(QWidget *parent)
|
|||||||
this, &AvdDialog::updateDeviceDefinitionComboBox);
|
this, &AvdDialog::updateDeviceDefinitionComboBox);
|
||||||
connect(m_abiComboBox, &QComboBox::currentIndexChanged,
|
connect(m_abiComboBox, &QComboBox::currentIndexChanged,
|
||||||
this, &AvdDialog::updateApiLevelComboBox);
|
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);
|
connect(m_buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
|
||||||
|
|
||||||
m_deviceTypeToStringMap.insert(AvdDialog::Phone, "Phone");
|
m_deviceTypeToStringMap.insert(AvdDialog::Phone, "Phone");
|
||||||
@@ -131,31 +133,6 @@ AvdDialog::AvdDialog(QWidget *parent)
|
|||||||
updateApiLevelComboBox();
|
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
|
bool AvdDialog::isValid() const
|
||||||
{
|
{
|
||||||
return !name().isEmpty() && systemImage() && systemImage()->isValid() && !abi().isEmpty();
|
return !name().isEmpty() && systemImage() && systemImage()->isValid() && !abi().isEmpty();
|
||||||
@@ -179,6 +156,63 @@ AvdDialog::DeviceType AvdDialog::tagToDeviceType(const QString &type_tag)
|
|||||||
return AvdDialog::PhoneOrTablet;
|
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)
|
static bool avdManagerCommand(const QStringList &args, QString *output)
|
||||||
{
|
{
|
||||||
CommandLine cmd(AndroidConfig::avdManagerToolPath(), args);
|
CommandLine cmd(AndroidConfig::avdManagerToolPath(), args);
|
||||||
|
@@ -5,6 +5,8 @@
|
|||||||
|
|
||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
|
|
||||||
|
#include <solutions/tasking/tasktreerunner.h>
|
||||||
|
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QRegularExpression>
|
#include <QRegularExpression>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
@@ -30,7 +32,6 @@ class AvdDialog : public QDialog
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit AvdDialog(QWidget *parent = nullptr);
|
explicit AvdDialog(QWidget *parent = nullptr);
|
||||||
int exec() override;
|
|
||||||
CreateAvdInfo avdInfo() const;
|
CreateAvdInfo avdInfo() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -50,6 +51,8 @@ private:
|
|||||||
|
|
||||||
static AvdDialog::DeviceType tagToDeviceType(const QString &type_tag);
|
static AvdDialog::DeviceType tagToDeviceType(const QString &type_tag);
|
||||||
|
|
||||||
|
void createAvd();
|
||||||
|
|
||||||
struct DeviceDefinitionStruct
|
struct DeviceDefinitionStruct
|
||||||
{
|
{
|
||||||
QString name_id;
|
QString name_id;
|
||||||
@@ -72,6 +75,7 @@ private:
|
|||||||
QComboBox *m_deviceDefinitionTypeComboBox;
|
QComboBox *m_deviceDefinitionTypeComboBox;
|
||||||
QCheckBox *m_overwriteCheckBox;
|
QCheckBox *m_overwriteCheckBox;
|
||||||
QDialogButtonBox *m_buttonBox;
|
QDialogButtonBox *m_buttonBox;
|
||||||
|
Tasking::TaskTreeRunner m_taskTreeRunner;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // Internal
|
} // Internal
|
||||||
|
Reference in New Issue
Block a user