diff --git a/src/plugins/android/androidavdmanager.cpp b/src/plugins/android/androidavdmanager.cpp index 664f2346200..c0ad0fa91e6 100644 --- a/src/plugins/android/androidavdmanager.cpp +++ b/src/plugins/android/androidavdmanager.cpp @@ -4,7 +4,6 @@ #include "androidavdmanager.h" #include "androidconfigurations.h" #include "androidtr.h" -#include "avdmanageroutputparser.h" #include @@ -44,71 +43,6 @@ bool AndroidAvdManager::avdManagerCommand(const QStringList &args, QString *outp return false; } -enum TagModification { CommentOut, Uncomment }; - -static void modifyManufacturerTag(const FilePath &avdPath, TagModification modification) -{ - if (!avdPath.exists()) - return; - - const FilePath configFilePath = avdPath / "config.ini"; - FileReader reader; - if (!reader.fetch(configFilePath, QIODevice::ReadOnly | QIODevice::Text)) - return; - - FileSaver saver(configFilePath); - QTextStream textStream(reader.data()); - while (!textStream.atEnd()) { - QString line = textStream.readLine(); - if (line.contains("hw.device.manufacturer")) { - if (modification == Uncomment) - line.replace("#", ""); - else - line.prepend("#"); - } - line.append("\n"); - saver.write(line.toUtf8()); - } - saver.finalize(); -} - -static AndroidDeviceInfoList listVirtualDevices() -{ - /* - Currenly avdmanager tool fails to parse some AVDs because the correct - device definitions at devices.xml does not have some of the newest devices. - Particularly, failing because of tag "hw.device.manufacturer", thus removing - it would make paring successful. However, it has to be returned afterwards, - otherwise, Android Studio would give an error during parsing also. So this fix - aim to keep support for Qt Creator and Android Studio. - */ - FilePaths allAvdErrorPaths; - while (true) { - QString output; - if (!AndroidAvdManager::avdManagerCommand({"list", "avd"}, &output)) { - qCDebug(avdManagerLog) - << "Avd list command failed" << output << androidConfig().sdkToolsVersion(); - return {}; - } - - const auto parsedAvdList = parseAvdList(output); - if (parsedAvdList.errorPaths.isEmpty()) { - for (const FilePath &avdPath : std::as_const(allAvdErrorPaths)) - modifyManufacturerTag(avdPath, Uncomment); - return parsedAvdList.avdList; - } - allAvdErrorPaths << parsedAvdList.errorPaths; - for (const FilePath &avdPath : parsedAvdList.errorPaths) - modifyManufacturerTag(avdPath, CommentOut); - } - return {}; -} - -QFuture AndroidAvdManager::avdList() const -{ - return Utils::asyncRun(listVirtualDevices); -} - QString AndroidAvdManager::startAvd(const QString &name) const { if (!findAvd(name).isEmpty() || startAvdAsync(name)) diff --git a/src/plugins/android/androidavdmanager.h b/src/plugins/android/androidavdmanager.h index 81686748edd..772951c058a 100644 --- a/src/plugins/android/androidavdmanager.h +++ b/src/plugins/android/androidavdmanager.h @@ -2,8 +2,6 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #pragma once -#include "androiddeviceinfo.h" - #include #include @@ -13,8 +11,6 @@ namespace Android::Internal { class AndroidAvdManager { public: - QFuture avdList() const; - QString startAvd(const QString &name) const; bool startAvdAsync(const QString &avdName) const; QString findAvd(const QString &avdName) const; diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index 2326a3b1058..1478abfb5fb 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -2,14 +2,16 @@ // Copyright (C) 2016 BogDan Vatra // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 +#include "androiddevice.h" + #include "androidavdmanager.h" #include "androidconfigurations.h" #include "androidconstants.h" -#include "androiddevice.h" #include "androidmanager.h" #include "androidsignaloperation.h" #include "androidtr.h" #include "avddialog.h" +#include "avdmanageroutputparser.h" #include @@ -21,6 +23,8 @@ #include #include +#include + #include #include #include @@ -410,10 +414,10 @@ void AndroidDevice::initAvdSettings() m_avdSettings.reset(new QSettings(configPath.toUserOutput(), QSettings::IniFormat)); } -void AndroidDeviceManager::updateAvdsList() +void AndroidDeviceManager::updateAvdList() { - if (!m_avdsFutureWatcher.isRunning() && androidConfig().adbToolPath().exists()) - m_avdsFutureWatcher.setFuture(m_avdManager.avdList()); + if (androidConfig().adbToolPath().exists()) + m_avdListRunner.start(m_avdListRecipe); } IDevice::DeviceState AndroidDeviceManager::getDeviceState(const QString &serial, @@ -687,7 +691,7 @@ void AndroidDeviceManager::setupDevicesWatcher() m_adbDeviceWatcherProcess->setStdErrLineCallback([](const QString &error) { qCDebug(androidDeviceLog) << "ADB device watcher error" << error; }); m_adbDeviceWatcherProcess->setStdOutLineCallback([this](const QString &output) { - HandleDevicesListChange(output); + handleDevicesListChange(output); }); const CommandLine command{androidConfig().adbToolPath(), {"track-devices"}}; @@ -699,17 +703,12 @@ void AndroidDeviceManager::setupDevicesWatcher() // Setup AVD filesystem watcher to listen for changes when an avd is created/deleted, // or started/stopped m_avdFileSystemWatcher.addPath(avdFilePath().toString()); - connect(&m_avdsFutureWatcher, &QFutureWatcherBase::finished, - this, &AndroidDeviceManager::HandleAvdsListChange); connect(&m_avdFileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, [this] { - if (m_avdPathGuard.isLocked()) - return; - // If the avd list upate command is running no need to call it again. - if (!m_avdsFutureWatcher.isRunning()) - updateAvdsList(); + if (!m_avdPathGuard.isLocked()) + updateAvdList(); }); // Call initial update - updateAvdsList(); + updateAvdList(); } IDevice::Ptr AndroidDeviceManager::createDeviceFromInfo(const CreateAvdInfo &info) @@ -731,7 +730,7 @@ IDevice::Ptr AndroidDeviceManager::createDeviceFromInfo(const CreateAvdInfo &inf return IDevice::Ptr(dev); } -void AndroidDeviceManager::HandleAvdsListChange() +void AndroidDeviceManager::handleAvdListChange(const AndroidDeviceInfoList &avdList) { DeviceManager *const devMgr = DeviceManager::instance(); @@ -744,7 +743,7 @@ void AndroidDeviceManager::HandleAvdsListChange() } QList connectedDevs; - for (const AndroidDeviceInfo &item : m_avdsFutureWatcher.result()) { + for (const AndroidDeviceInfo &item : avdList) { const Id deviceId = AndroidDevice::idFromDeviceInfo(item); const QString displayName = AndroidDevice::displayNameFromInfo(item); IDevice::ConstPtr dev = devMgr->find(deviceId); @@ -803,7 +802,7 @@ void AndroidDeviceManager::HandleAvdsListChange() } } -void AndroidDeviceManager::HandleDevicesListChange(const QString &serialNumber) +void AndroidDeviceManager::handleDevicesListChange(const QString &serialNumber) { DeviceManager *const devMgr = DeviceManager::instance(); const QStringList serialBits = serialNumber.split('\t'); @@ -879,16 +878,93 @@ AndroidDeviceManager *AndroidDeviceManager::instance() return s_instance; } +enum TagModification { CommentOut, Uncomment }; + +static void modifyManufacturerTag(const FilePath &avdPath, TagModification modification) +{ + if (!avdPath.exists()) + return; + + const FilePath configFilePath = avdPath / "config.ini"; + FileReader reader; + if (!reader.fetch(configFilePath, QIODevice::ReadOnly | QIODevice::Text)) + return; + + FileSaver saver(configFilePath); + QTextStream textStream(reader.data()); + while (!textStream.atEnd()) { + QString line = textStream.readLine(); + if (line.contains("hw.device.manufacturer")) { + if (modification == Uncomment) + line.replace("#", ""); + else + line.prepend("#"); + } + line.append("\n"); + saver.write(line.toUtf8()); + } + saver.finalize(); +} + AndroidDeviceManager::AndroidDeviceManager(QObject *parent) : QObject(parent) + , m_avdListRecipe{} { QTC_ASSERT(!s_instance, return); s_instance = this; + + using namespace Tasking; + + const Storage storage; + + const LoopUntil iterator([storage](int iteration) { + return iteration == 0 || storage->count() > 0; + }); + + const auto onProcessSetup = [](Process &process) { + const CommandLine cmd(androidConfig().avdManagerToolPath(), {"list", "avd"}); + qCDebug(androidDeviceLog).noquote() << "Running AVD Manager command:" << cmd.toUserOutput(); + process.setEnvironment(androidConfig().toolsEnvironment()); + process.setCommand(cmd); + }; + const auto onProcessDone = [this, storage](const Process &process, DoneWith result) { + const QString output = process.allOutput(); + if (result != DoneWith::Success) { + qCDebug(androidDeviceLog) + << "Avd list command failed" << output << androidConfig().sdkToolsVersion(); + return DoneResult::Error; + } + + const auto parsedAvdList = parseAvdList(output); + if (parsedAvdList.errorPaths.isEmpty()) { + for (const FilePath &avdPath : *storage) + modifyManufacturerTag(avdPath, Uncomment); + storage->clear(); // Don't repeat anymore + handleAvdListChange(parsedAvdList.avdList); + } else { + for (const FilePath &avdPath : parsedAvdList.errorPaths) + modifyManufacturerTag(avdPath, CommentOut); + storage->append(parsedAvdList.errorPaths); + } + return DoneResult::Success; // Repeat + }; + + // Currenly avdmanager tool fails to parse some AVDs because the correct + // device definitions at devices.xml does not have some of the newest devices. + // Particularly, failing because of tag "hw.device.manufacturer", thus removing + // it would make paring successful. However, it has to be returned afterwards, + // otherwise, Android Studio would give an error during parsing also. So this fix + // aim to keep support for Qt Creator and Android Studio. + + m_avdListRecipe = Group { + storage, + iterator, + ProcessTask(onProcessSetup, onProcessDone) + }; } AndroidDeviceManager::~AndroidDeviceManager() { - m_avdsFutureWatcher.waitForFinished(); QTC_ASSERT(s_instance == this, return); s_instance = nullptr; } diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index b21958d1d16..144be6a2600 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -11,9 +11,10 @@ #include #include +#include + #include -#include #include #include @@ -76,7 +77,7 @@ class AndroidDeviceManager : public QObject public: static AndroidDeviceManager *instance(); void setupDevicesWatcher(); - void updateAvdsList(); + void updateAvdList(); IDevice::DeviceState getDeviceState(const QString &serial, IDevice::MachineType type) const; void updateDeviceState(const ProjectExplorer::IDevice::ConstPtr &device); @@ -95,12 +96,13 @@ private: explicit AndroidDeviceManager(QObject *parent); ~AndroidDeviceManager(); - void HandleDevicesListChange(const QString &serialNumber); - void HandleAvdsListChange(); + void handleDevicesListChange(const QString &serialNumber); + void handleAvdListChange(const AndroidDeviceInfoList &avdList); QString emulatorName(const QString &serialNumber) const; - QFutureWatcher m_avdsFutureWatcher; + Tasking::Group m_avdListRecipe; + Tasking::TaskTreeRunner m_avdListRunner; std::unique_ptr m_removeAvdProcess; QFileSystemWatcher m_avdFileSystemWatcher; Utils::Guard m_avdPathGuard; diff --git a/src/plugins/android/avddialog.cpp b/src/plugins/android/avddialog.cpp index 0f92d4bef6b..e1f7dd8c4b7 100644 --- a/src/plugins/android/avddialog.cpp +++ b/src/plugins/android/avddialog.cpp @@ -141,7 +141,7 @@ int AvdDialog::exec() return QDialog::Rejected; } m_createdAvdInfo = avdInfo; - AndroidDeviceManager::instance()->updateAvdsList(); + AndroidDeviceManager::instance()->updateAvdList(); } return execResult; }