Android: Use avdmanager tool

android tool is deprecated since sdk tools version 25.3.0.
Use the new avdmanager tool

Task-number: QTCREATORBUG-17814
Change-Id: Id6f495f14e12d0069df08164cac1929b76d9e932
Reviewed-by: BogDan Vatra <bogdan@kdab.com>
This commit is contained in:
Vikas Pachdha
2017-04-03 11:11:17 +02:00
parent 05b77e8468
commit 70be880bcb
17 changed files with 599 additions and 145 deletions

View File

@@ -49,7 +49,8 @@ HEADERS += \
androidbuildapkwidget.h \ androidbuildapkwidget.h \
androidrunnable.h \ androidrunnable.h \
androidtoolmanager.h \ androidtoolmanager.h \
androidsdkmanager.h androidsdkmanager.h \
androidavdmanager.h
SOURCES += \ SOURCES += \
androidconfigurations.cpp \ androidconfigurations.cpp \
@@ -92,7 +93,8 @@ SOURCES += \
androidqtsupport.cpp \ androidqtsupport.cpp \
androidrunnable.cpp \ androidrunnable.cpp \
androidtoolmanager.cpp \ androidtoolmanager.cpp \
androidsdkmanager.cpp androidsdkmanager.cpp \
androidavdmanager.cpp
FORMS += \ FORMS += \
androidsettingswidget.ui \ androidsettingswidget.ui \

View File

@@ -22,6 +22,8 @@ Project {
"android.qrc", "android.qrc",
"androidanalyzesupport.cpp", "androidanalyzesupport.cpp",
"androidanalyzesupport.h", "androidanalyzesupport.h",
"androidavdmanager.cpp",
"androidavdmanager.h",
"androidconfigurations.cpp", "androidconfigurations.cpp",
"androidconfigurations.h", "androidconfigurations.h",
"androidconstants.h", "androidconstants.h",

View File

@@ -0,0 +1,441 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "androidavdmanager.h"
#include "androidtoolmanager.h"
#include "utils/algorithm.h"
#include "utils/qtcassert.h"
#include "utils/runextensions.h"
#include "utils/synchronousprocess.h"
#include <QApplication>
#include <QFileInfo>
#include <QLoggingCategory>
#include <QSettings>
#include <chrono>
namespace {
Q_LOGGING_CATEGORY(avdManagerLog, "qtc.android.avdManager")
}
namespace Android {
namespace Internal {
using namespace std;
// Avd list keys to parse avd
const char avdInfoNameKey[] = "Name:";
const char avdInfoPathKey[] = "Path:";
const char avdInfoAbiKey[] = "abi.type";
const char avdInfoTargetKey[] = "target";
const char avdInfoErrorKey[] = "Error:";
const QVersionNumber avdManagerIntroVersion(25, 3 ,0);
const int avdCreateTimeoutMs = 30000;
/*!
Runs the \c avdmanager tool specific to configuration \a config with arguments \a args. Returns
\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)
{
QString avdManagerToolPath = config.avdManagerToolPath().toString();
Utils::SynchronousProcess proc;
Utils::SynchronousProcessResponse response = proc.runBlocking(avdManagerToolPath, args);
if (response.result == Utils::SynchronousProcessResponse::Finished) {
if (output)
*output = response.allOutput();
return true;
}
return false;
}
/*!
Parses the \a line for a [spaces]key[spaces]value[spaces] pattern and returns
\c true if the key is found, \c false otherwise. The value is copied into \a value.
*/
static bool valueForKey(QString key, const QString &line, QString *value = nullptr)
{
auto trimmedInput = line.trimmed();
if (trimmedInput.startsWith(key)) {
if (value)
*value = trimmedInput.section(key, 1, 1).trimmed();
return true;
}
return false;
}
static bool checkForTimeout(const chrono::steady_clock::time_point &start,
int msecs = 3000)
{
bool timedOut = false;
auto end = chrono::steady_clock::now();
if (chrono::duration_cast<chrono::milliseconds>(end-start).count() > msecs)
timedOut = true;
return timedOut;
}
static AndroidConfig::CreateAvdInfo createAvdCommand(const AndroidConfig config,
const AndroidConfig::CreateAvdInfo &info)
{
AndroidConfig::CreateAvdInfo result = info;
if (!result.isValid()) {
qCDebug(avdManagerLog) << "AVD Create failed. Invalid CreateAvdInfo" << result.name
<< result.target.name << result.target.apiLevel;
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Invalid input.");
return result;
}
QStringList arguments({"create", "avd", "-k", result.target.package, "-n", result.name});
if (!result.abi.isEmpty()) {
SystemImage image = Utils::findOrDefault(result.target.systemImages,
Utils::equal(&SystemImage::abiName, result.abi));
if (image.isValid()) {
arguments << "-k" << image.package;
} else {
qCDebug(avdManagerLog) << "AVD Create failed. Cannot find system image for the platform"
<< result.abi << result.target.name;
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Cannot find system image for "
"the ABI %1(%2).").arg(result.abi).arg(result.target.name);
return result;
}
} else {
arguments << "-k" << result.target.package;
}
if (result.sdcardSize > 0)
arguments << "-c" << QString::fromLatin1("%1M").arg(result.sdcardSize);
QProcess proc;
proc.start(config.avdManagerToolPath().toString(), arguments);
if (!proc.waitForStarted()) {
result.error = QApplication::translate("AndroidAvdManager",
"Could not start process \"%1 %2\"")
.arg(config.avdManagerToolPath().toString(), arguments.join(' '));
return result;
}
QTC_CHECK(proc.state() == QProcess::Running);
proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"
auto start = chrono::steady_clock::now();
QString errorOutput;
QByteArray question;
while (errorOutput.isEmpty()) {
proc.waitForReadyRead(500);
question += proc.readAllStandardOutput();
if (question.endsWith(QByteArray("]:"))) {
// truncate to last line
int index = question.lastIndexOf(QByteArray("\n"));
if (index != -1)
question = question.mid(index);
if (question.contains("hw.gpu.enabled"))
proc.write(QByteArray("yes\n"));
else
proc.write(QByteArray("\n"));
question.clear();
}
// The exit code is always 0, so we need to check stderr
// For now assume that any output at all indicates a error
errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
if (proc.state() != QProcess::Running)
break;
// For a sane input and command, process should finish before timeout.
if (checkForTimeout(start, avdCreateTimeoutMs)) {
result.error = QApplication::translate("AndroidAvdManager",
"Cannot create AVD. Command timed out.");
}
}
// Kill the running process.
if (proc.state() != QProcess::NotRunning) {
proc.terminate();
if (!proc.waitForFinished(3000))
proc.kill();
}
QTC_CHECK(proc.state() == QProcess::NotRunning);
result.error = errorOutput;
return result;
}
/*!
\class AvdManagerOutputParser
\brief The AvdManagerOutputParser class is a helper class to parse the output of the avdmanager
commands.
*/
class AvdManagerOutputParser
{
public:
AndroidDeviceInfoList listVirtualDevices(const AndroidConfig &config);
AndroidDeviceInfoList parseAvdList(const QString &output);
private:
bool parseAvd(const QStringList &deviceInfo, AndroidDeviceInfo *avd);
};
AndroidAvdManager::AndroidAvdManager(const AndroidConfig &config):
m_config(config),
m_androidTool(new AndroidToolManager(m_config)),
m_parser(new AvdManagerOutputParser)
{
}
AndroidAvdManager::~AndroidAvdManager()
{
}
bool AndroidAvdManager::avdManagerUiToolAvailable() const
{
return m_config.sdkToolsVersion() < avdManagerIntroVersion;
}
void AndroidAvdManager::launchAvdManagerUiTool() const
{
if (avdManagerUiToolAvailable()) {
m_androidTool->launchAvdManager();
} else {
qCDebug(avdManagerLog) << "AVD Ui tool launch failed. UI tool not available"
<< m_config.sdkToolsVersion();
}
}
QFuture<AndroidConfig::CreateAvdInfo>
AndroidAvdManager::createAvd(AndroidConfig::CreateAvdInfo info) const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
return m_androidTool->createAvd(info);
return Utils::runAsync(&createAvdCommand, m_config, info);
}
bool AndroidAvdManager::removeAvd(const QString &name) const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
return m_androidTool->removeAvd(name);
Utils::SynchronousProcess proc;
proc.setTimeoutS(5);
Utils::SynchronousProcessResponse response
= proc.runBlocking(m_config.avdManagerToolPath().toString(),
QStringList({"delete", "avd", "-n", name}));
return response.result == Utils::SynchronousProcessResponse::Finished && response.exitCode == 0;
}
QFuture<AndroidDeviceInfoList> AndroidAvdManager::avdList() const
{
if (m_config.sdkToolsVersion() < avdManagerIntroVersion)
return m_androidTool->androidVirtualDevicesFuture();
return Utils::runAsync(&AvdManagerOutputParser::listVirtualDevices, m_parser.get(), m_config);
}
QString AndroidAvdManager::startAvd(const QString &name) const
{
if (!findAvd(name).isEmpty() || startAvdAsync(name))
return waitForAvd(name);
return QString();
}
bool AndroidAvdManager::startAvdAsync(const QString &avdName) const
{
QProcess *avdProcess = new QProcess();
QObject::connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
avdProcess, &QObject::deleteLater);
// start the emulator
QStringList arguments;
if (AndroidConfigurations::force32bitEmulator())
arguments << "-force-32bit";
arguments << "-partition-size" << QString::number(m_config.partitionSize())
<< "-avd" << avdName;
avdProcess->start(m_config.emulatorToolPath().toString(), arguments);
if (!avdProcess->waitForStarted(-1)) {
delete avdProcess;
return false;
}
return true;
}
QString AndroidAvdManager::findAvd(const QString &avdName) const
{
QVector<AndroidDeviceInfo> devices = m_config.connectedDevices();
foreach (AndroidDeviceInfo device, devices) {
if (device.type != AndroidDeviceInfo::Emulator)
continue;
if (device.avdname == avdName)
return device.serialNumber;
}
return QString();
}
QString AndroidAvdManager::waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi) const
{
// we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running
// 60 rounds of 2s sleeping, two minutes for the avd to start
QString serialNumber;
for (int i = 0; i < 60; ++i) {
if (fi.isCanceled())
return QString();
serialNumber = findAvd(avdName);
if (!serialNumber.isEmpty())
return waitForBooted(serialNumber, fi) ? serialNumber : QString();
QThread::sleep(2);
}
return QString();
}
bool AndroidAvdManager::isAvdBooted(const QString &device) const
{
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
arguments << "shell" << "getprop" << "init.svc.bootanim";
Utils::SynchronousProcess adbProc;
adbProc.setTimeoutS(10);
Utils::SynchronousProcessResponse response =
adbProc.runBlocking(m_config.adbToolPath().toString(), arguments);
if (response.result != Utils::SynchronousProcessResponse::Finished)
return false;
QString value = response.allOutput().trimmed();
return value == "stopped";
}
bool AndroidAvdManager::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
{
// found a serial number, now wait until it's done booting...
for (int i = 0; i < 60; ++i) {
if (fi.isCanceled())
return false;
if (isAvdBooted(serialNumber)) {
return true;
} else {
QThread::sleep(2);
if (!m_config.isConnected(serialNumber)) // device was disconnected
return false;
}
}
return false;
}
AndroidDeviceInfoList AvdManagerOutputParser::listVirtualDevices(const AndroidConfig &config)
{
QString output;
if (!avdManagerCommand(config, QStringList({"list", "avd"}), &output)) {
qCDebug(avdManagerLog) << "Avd list command failed" << output << config.sdkToolsVersion();
return {};
}
return parseAvdList(output);
}
AndroidDeviceInfoList AvdManagerOutputParser::parseAvdList(const QString &output)
{
AndroidDeviceInfoList avdList;
QStringList avdInfo;
auto parseAvdInfo = [&avdInfo, &avdList, this] () {
AndroidDeviceInfo avd;
if (parseAvd(avdInfo, &avd)) {
// armeabi-v7a devices can also run armeabi code
if (avd.cpuAbi.contains("armeabi-v7a"))
avd.cpuAbi << "armeabi";
avd.state = AndroidDeviceInfo::OkState;
avd.type = AndroidDeviceInfo::Emulator;
avdList << avd;
} else {
qCDebug(avdManagerLog) << "Avd Parsing: Parsing failed: " << avdInfo;
}
avdInfo.clear();
};
foreach (QString line, output.split('\n')) {
if (line.startsWith("---------") || line.isEmpty()) {
parseAvdInfo();
} else {
avdInfo << line;
}
}
if (!avdInfo.isEmpty())
parseAvdInfo();
Utils::sort(avdList);
return avdList;
}
bool AvdManagerOutputParser::parseAvd(const QStringList &deviceInfo, AndroidDeviceInfo *avd)
{
QTC_ASSERT(avd, return false);
foreach (const QString &line, deviceInfo) {
QString value;
if (valueForKey(avdInfoErrorKey, line)) {
qCDebug(avdManagerLog) << "Avd Parsing: Skip avd device. Error key found:" << line;
return false;
} else if (valueForKey(avdInfoNameKey, line, &value)) {
avd->avdname = value;
} else if (valueForKey(avdInfoPathKey, line, &value)) {
const Utils::FileName avdPath = Utils::FileName::fromString(value);
if (avdPath.exists())
{
// Get ABI.
Utils::FileName configFile = avdPath;
configFile.appendPath("config.ini");
QSettings config(configFile.toString(), QSettings::IniFormat);
value = config.value(avdInfoAbiKey).toString();
if (!value.isEmpty())
avd->cpuAbi << value;
else
qCDebug(avdManagerLog) << "Avd Parsing: Cannot find ABI:" << configFile;
// Get Target
Utils::FileName avdInfoFile = avdPath.parentDir();
QString avdInfoFileName = avdPath.toFileInfo().baseName() + ".ini";
avdInfoFile.appendPath(avdInfoFileName);
QSettings avdInfo(avdInfoFile.toString(), QSettings::IniFormat);
value = avdInfo.value(avdInfoTargetKey).toString();
if (!value.isEmpty())
avd->sdk = value.section('-', -1).toInt();
else
qCDebug(avdManagerLog) << "Avd Parsing: Cannot find sdk API:" << avdInfoFile.toString();
}
}
}
return true;
}
} // namespace Internal
} // namespace Android

View File

@@ -0,0 +1,66 @@
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "androidconfigurations.h"
#include <memory>
namespace Android {
namespace Internal {
class AndroidToolManager;
class AvdManagerOutputParser;
class AndroidAvdManager
{
public:
AndroidAvdManager(const AndroidConfig& config = AndroidConfigurations::currentConfig());
~AndroidAvdManager();
bool avdManagerUiToolAvailable() const;
void launchAvdManagerUiTool() const;
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
bool removeAvd(const QString &name) const;
QFuture<AndroidDeviceInfoList> avdList() const;
QString startAvd(const QString &name) const;
bool startAvdAsync(const QString &avdName) const;
QString findAvd(const QString &avdName) const;
QString waitForAvd(const QString &avdName,
const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const;
bool isAvdBooted(const QString &device) const;
private:
bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const;
private:
const AndroidConfig &m_config;
std::unique_ptr<AndroidToolManager> m_androidTool;
std::unique_ptr<AvdManagerOutputParser> m_parser;
};
} // namespace Internal
} // namespace Android

View File

@@ -386,7 +386,10 @@ FileName AndroidConfig::antToolPath() const
FileName AndroidConfig::emulatorToolPath() const FileName AndroidConfig::emulatorToolPath() const
{ {
FileName path = m_sdkLocation; FileName path = m_sdkLocation;
return path.appendPath(QLatin1String("tools/emulator" QTC_HOST_EXE_SUFFIX)); QString relativePath = "emulator/emulator";
if (sdkToolsVersion() < QVersionNumber(25, 3, 0))
relativePath = "tools/emulator";
return path.appendPath(relativePath + QTC_HOST_EXE_SUFFIX);
} }
FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVersion) const FileName AndroidConfig::toolPath(const Abi &abi, const QString &ndkToolChainVersion) const
@@ -409,6 +412,16 @@ FileName AndroidConfig::sdkManagerToolPath() const
return sdkPath; return sdkPath;
} }
FileName AndroidConfig::avdManagerToolPath() const
{
FileName avdManagerPath = m_sdkLocation;
QString toolPath = "tools/bin/avdmanager";
if (HostOsInfo::isWindowsHost())
toolPath += ANDROID_BAT_SUFFIX;
avdManagerPath = avdManagerPath.appendPath(toolPath);
return avdManagerPath;
}
FileName AndroidConfig::gccPath(const Abi &abi, Core::Id lang, FileName AndroidConfig::gccPath(const Abi &abi, Core::Id lang,
const QString &ndkToolChainVersion) const const QString &ndkToolChainVersion) const
{ {
@@ -515,46 +528,6 @@ AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent,
return result; return result;
} }
QString AndroidConfig::startAVD(const QString &name) const
{
if (!findAvd(name).isEmpty() || startAVDAsync(name))
return waitForAvd(name);
return QString();
}
bool AndroidConfig::startAVDAsync(const QString &avdName) const
{
QProcess *avdProcess = new QProcess();
QObject::connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
avdProcess, &QObject::deleteLater);
// start the emulator
QStringList arguments;
if (AndroidConfigurations::force32bitEmulator())
arguments << QLatin1String("-force-32bit");
arguments << QLatin1String("-partition-size") << QString::number(partitionSize())
<< QLatin1String("-avd") << avdName;
avdProcess->start(emulatorToolPath().toString(), arguments);
if (!avdProcess->waitForStarted(-1)) {
delete avdProcess;
return false;
}
return true;
}
QString AndroidConfig::findAvd(const QString &avdName) const
{
QVector<AndroidDeviceInfo> devices = connectedDevices();
foreach (AndroidDeviceInfo device, devices) {
if (device.type != AndroidDeviceInfo::Emulator)
continue;
if (device.avdname == avdName)
return device.serialNumber;
}
return QString();
}
bool AndroidConfig::isConnected(const QString &serialNumber) const bool AndroidConfig::isConnected(const QString &serialNumber) const
{ {
QVector<AndroidDeviceInfo> devices = connectedDevices(); QVector<AndroidDeviceInfo> devices = connectedDevices();
@@ -565,39 +538,6 @@ bool AndroidConfig::isConnected(const QString &serialNumber) const
return false; return false;
} }
bool AndroidConfig::waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const
{
// found a serial number, now wait until it's done booting...
for (int i = 0; i < 60; ++i) {
if (fi.isCanceled())
return false;
if (hasFinishedBooting(serialNumber)) {
return true;
} else {
QThread::sleep(2);
if (!isConnected(serialNumber)) // device was disconnected
return false;
}
}
return false;
}
QString AndroidConfig::waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi) const
{
// we cannot use adb -e wait-for-device, since that doesn't work if a emulator is already running
// 60 rounds of 2s sleeping, two minutes for the avd to start
QString serialNumber;
for (int i = 0; i < 60; ++i) {
if (fi.isCanceled())
return QString();
serialNumber = findAvd(avdName);
if (!serialNumber.isEmpty())
return waitForBooted(serialNumber, fi) ? serialNumber : QString();
QThread::sleep(2);
}
return QString();
}
bool AndroidConfig::isBootToQt(const QString &device) const bool AndroidConfig::isBootToQt(const QString &device) const
{ {
return isBootToQt(adbToolPath().toString(), device); return isBootToQt(adbToolPath().toString(), device);
@@ -720,21 +660,6 @@ QString AndroidConfig::getProductModel(const QString &device) const
return model; return model;
} }
bool AndroidConfig::hasFinishedBooting(const QString &device) const
{
QStringList arguments = AndroidDeviceInfo::adbSelector(device);
arguments << QLatin1String("shell") << QLatin1String("getprop")
<< QLatin1String("init.svc.bootanim");
SynchronousProcess adbProc;
adbProc.setTimeoutS(10);
SynchronousProcessResponse response = adbProc.runBlocking(adbToolPath().toString(), arguments);
if (response.result != SynchronousProcessResponse::Finished)
return false;
QString value = response.allOutput().trimmed();
return value == QLatin1String("stopped");
}
QStringList AndroidConfig::getAbis(const QString &device) const QStringList AndroidConfig::getAbis(const QString &device) const
{ {
return getAbis(adbToolPath().toString(), device); return getAbis(adbToolPath().toString(), device);

View File

@@ -72,6 +72,7 @@ public:
bool isValid() const { return !serialNumber.isEmpty() || !avdname.isEmpty(); } bool isValid() const { return !serialNumber.isEmpty() || !avdname.isEmpty(); }
bool operator<(const AndroidDeviceInfo &other) const; bool operator<(const AndroidDeviceInfo &other) const;
}; };
using AndroidDeviceInfoList = QList<AndroidDeviceInfo>;
//! Defines an Android system image. //! Defines an Android system image.
class SystemImage class SystemImage
@@ -89,6 +90,7 @@ using SystemImageList = QList<SystemImage>;
class SdkPlatform class SdkPlatform
{ {
public: public:
bool isValid() const { return !name.isEmpty() && apiLevel != -1; }
bool operator <(const SdkPlatform &other) const; bool operator <(const SdkPlatform &other) const;
int apiLevel = -1; int apiLevel = -1;
QString name; QString name;
@@ -141,6 +143,7 @@ public:
Utils::FileName antToolPath() const; Utils::FileName antToolPath() const;
Utils::FileName emulatorToolPath() const; Utils::FileName emulatorToolPath() const;
Utils::FileName sdkManagerToolPath() const; Utils::FileName sdkManagerToolPath() const;
Utils::FileName avdManagerToolPath() const;
Utils::FileName gccPath(const ProjectExplorer::Abi &abi, Core::Id lang, Utils::FileName gccPath(const ProjectExplorer::Abi &abi, Core::Id lang,
const QString &ndkToolChainVersion) const; const QString &ndkToolChainVersion) const;
@@ -152,7 +155,8 @@ public:
class CreateAvdInfo class CreateAvdInfo
{ {
public: public:
QString target; bool isValid() const { return target.isValid() && !name.isEmpty(); }
SdkPlatform target;
QString name; QString name;
QString abi; QString abi;
int sdcardSize = 0; int sdcardSize = 0;
@@ -164,10 +168,6 @@ public:
QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const; QVector<AndroidDeviceInfo> connectedDevices(QString *error = 0) const;
static QVector<AndroidDeviceInfo> connectedDevices(const QString &adbToolPath, QString *error = 0); static QVector<AndroidDeviceInfo> connectedDevices(const QString &adbToolPath, QString *error = 0);
QString startAVD(const QString &name) const;
bool startAVDAsync(const QString &avdName) const;
QString findAvd(const QString &avdName) const;
QString waitForAvd(const QString &avdName, const QFutureInterface<bool> &fi = QFutureInterface<bool>()) const;
QString bestNdkPlatformMatch(int target) const; QString bestNdkPlatformMatch(int target) const;
static ProjectExplorer::Abi abiForToolChainPrefix(const QString &toolchainPrefix); static ProjectExplorer::Abi abiForToolChainPrefix(const QString &toolchainPrefix);
@@ -178,8 +178,6 @@ public:
QString getProductModel(const QString &device) const; QString getProductModel(const QString &device) const;
enum class OpenGl { Enabled, Disabled, Unknown }; enum class OpenGl { Enabled, Disabled, Unknown };
OpenGl getOpenGLEnabled(const QString &emulator) const; OpenGl getOpenGLEnabled(const QString &emulator) const;
bool hasFinishedBooting(const QString &device) const;
bool waitForBooted(const QString &serialNumber, const QFutureInterface<bool> &fi) const;
bool isConnected(const QString &serialNumber) const; bool isConnected(const QString &serialNumber) const;
SdkPlatform highestAndroidSdk() const; SdkPlatform highestAndroidSdk() const;
@@ -257,3 +255,5 @@ private:
}; };
} // namespace Android } // namespace Android
Q_DECLARE_METATYPE(Android::SdkPlatform)

View File

@@ -33,6 +33,7 @@
#include "androidmanager.h" #include "androidmanager.h"
#include "androidconstants.h" #include "androidconstants.h"
#include "androidglobal.h" #include "androidglobal.h"
#include "androidavdmanager.h"
#include <coreplugin/fileutils.h> #include <coreplugin/fileutils.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
@@ -262,8 +263,9 @@ bool AndroidDeployQtStep::init(QList<const BuildStep *> &earlierSteps)
m_adbPath = AndroidConfigurations::currentConfig().adbToolPath().toString(); m_adbPath = AndroidConfigurations::currentConfig().adbToolPath().toString();
if (AndroidConfigurations::currentConfig().findAvd(m_avdName).isEmpty()) AndroidAvdManager avdManager;
AndroidConfigurations::currentConfig().startAVDAsync(m_avdName); if (avdManager.findAvd(m_avdName).isEmpty())
avdManager.startAvdAsync(m_avdName);
return true; return true;
} }
@@ -414,7 +416,7 @@ void AndroidDeployQtStep::slotSetSerialNumber(const QString &serialNumber)
void AndroidDeployQtStep::run(QFutureInterface<bool> &fi) void AndroidDeployQtStep::run(QFutureInterface<bool> &fi)
{ {
if (!m_avdName.isEmpty()) { if (!m_avdName.isEmpty()) {
QString serialNumber = AndroidConfigurations::currentConfig().waitForAvd(m_avdName, fi); QString serialNumber = AndroidAvdManager().waitForAvd(m_avdName, fi);
if (serialNumber.isEmpty()) { if (serialNumber.isEmpty()) {
reportRunResult(fi, false); reportRunResult(fi, false);
return; return;

View File

@@ -25,6 +25,7 @@
#include "androiddevicedialog.h" #include "androiddevicedialog.h"
#include "androidmanager.h" #include "androidmanager.h"
#include "androidavdmanager.h"
#include "ui_androiddevicedialog.h" #include "ui_androiddevicedialog.h"
#include <utils/environment.h> #include <utils/environment.h>
@@ -424,7 +425,7 @@ AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi, Andro
m_apiLevel(apiLevel), m_apiLevel(apiLevel),
m_abi(abi), m_abi(abi),
m_defaultDevice(serialNumber), m_defaultDevice(serialNumber),
m_androidToolManager(new AndroidToolManager(AndroidConfigurations::currentConfig())) m_avdManager(new AndroidAvdManager)
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
m_ui->deviceView->setModel(m_model); m_ui->deviceView->setModel(m_model);
@@ -516,7 +517,7 @@ void AndroidDeviceDialog::refreshDeviceList()
m_ui->refreshDevicesButton->setEnabled(false); m_ui->refreshDevicesButton->setEnabled(false);
m_progressIndicator->show(); m_progressIndicator->show();
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString()); m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString());
m_futureWatcherRefreshDevices.setFuture(m_androidToolManager->androidVirtualDevicesFuture()); m_futureWatcherRefreshDevices.setFuture(m_avdManager->avdList());
} }
void AndroidDeviceDialog::devicesRefreshed() void AndroidDeviceDialog::devicesRefreshed()
@@ -531,7 +532,7 @@ void AndroidDeviceDialog::devicesRefreshed()
serialNumber = deviceType == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname; serialNumber = deviceType == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname;
} }
QVector<AndroidDeviceInfo> devices = m_futureWatcherRefreshDevices.result(); AndroidDeviceInfoList devices = m_futureWatcherRefreshDevices.result();
QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices, QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices,
[] (const AndroidDeviceInfo &info) { [] (const AndroidDeviceInfo &info) {
return info.avdname; return info.avdname;
@@ -584,12 +585,12 @@ void AndroidDeviceDialog::createAvd()
m_ui->createAVDButton->setEnabled(false); m_ui->createAVDButton->setEnabled(false);
AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi); AndroidConfig::CreateAvdInfo info = AndroidConfigurations::currentConfig().gatherCreateAVDInfo(this, m_apiLevel, m_abi);
if (info.target.isEmpty()) { if (!info.target.isValid()) {
m_ui->createAVDButton->setEnabled(true); m_ui->createAVDButton->setEnabled(true);
return; return;
} }
m_futureWatcherAddDevice.setFuture(m_androidToolManager->createAvd(info)); m_futureWatcherAddDevice.setFuture(m_avdManager->createAvd(info));
} }
void AndroidDeviceDialog::avdAdded() void AndroidDeviceDialog::avdAdded()

View File

@@ -26,7 +26,6 @@
#pragma once #pragma once
#include "androidconfigurations.h" #include "androidconfigurations.h"
#include "androidtoolmanager.h"
#include <QVector> #include <QVector>
#include <QDialog> #include <QDialog>
@@ -44,6 +43,7 @@ namespace Utils { class ProgressIndicator; }
namespace Android { namespace Android {
namespace Internal { namespace Internal {
class AndroidAvdManager;
class AndroidDeviceModel; class AndroidDeviceModel;
namespace Ui { class AndroidDeviceDialog; } namespace Ui { class AndroidDeviceDialog; }
@@ -77,10 +77,10 @@ private:
QString m_abi; QString m_abi;
QString m_avdNameFromAdd; QString m_avdNameFromAdd;
QString m_defaultDevice; QString m_defaultDevice;
std::unique_ptr<AndroidToolManager> m_androidToolManager; std::unique_ptr<AndroidAvdManager> m_avdManager;
QVector<AndroidDeviceInfo> m_connectedDevices; QVector<AndroidDeviceInfo> m_connectedDevices;
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice; QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
QFutureWatcher<QVector<AndroidDeviceInfo>> m_futureWatcherRefreshDevices; QFutureWatcher<AndroidDeviceInfoList> m_futureWatcherRefreshDevices;
}; };
} }

View File

@@ -34,6 +34,7 @@
#include "androidqtsupport.h" #include "androidqtsupport.h"
#include "androidqtversion.h" #include "androidqtversion.h"
#include "androidbuildapkstep.h" #include "androidbuildapkstep.h"
#include "androidavdmanager.h"
#include <coreplugin/documentmanager.h> #include <coreplugin/documentmanager.h>
#include <coreplugin/messagemanager.h> #include <coreplugin/messagemanager.h>
@@ -345,7 +346,7 @@ void AndroidManager::cleanLibsOnDevice(ProjectExplorer::Target *target)
QString deviceSerialNumber = info.serialNumber; QString deviceSerialNumber = info.serialNumber;
if (info.type == AndroidDeviceInfo::Emulator) { if (info.type == AndroidDeviceInfo::Emulator) {
deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname); deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
if (deviceSerialNumber.isEmpty()) if (deviceSerialNumber.isEmpty())
Core::MessageManager::write(tr("Starting Android virtual device failed.")); Core::MessageManager::write(tr("Starting Android virtual device failed."));
} }
@@ -374,7 +375,7 @@ void AndroidManager::installQASIPackage(ProjectExplorer::Target *target, const Q
QString deviceSerialNumber = info.serialNumber; QString deviceSerialNumber = info.serialNumber;
if (info.type == AndroidDeviceInfo::Emulator) { if (info.type == AndroidDeviceInfo::Emulator) {
deviceSerialNumber = AndroidConfigurations::currentConfig().startAVD(info.avdname); deviceSerialNumber = AndroidAvdManager().startAvd(info.avdname);
if (deviceSerialNumber.isEmpty()) if (deviceSerialNumber.isEmpty())
Core::MessageManager::write(tr("Starting Android virtual device failed.")); Core::MessageManager::write(tr("Starting Android virtual device failed."));
} }

View File

@@ -31,6 +31,7 @@
#include "androidglobal.h" #include "androidglobal.h"
#include "androidrunconfiguration.h" #include "androidrunconfiguration.h"
#include "androidmanager.h" #include "androidmanager.h"
#include "androidavdmanager.h"
#include <debugger/debuggerrunconfigurationaspect.h> #include <debugger/debuggerrunconfigurationaspect.h>
#include <projectexplorer/projectexplorer.h> #include <projectexplorer/projectexplorer.h>
@@ -791,8 +792,9 @@ void AndroidRunner::launchAVD()
emit adbParametersChanged(m_androidRunnable.packageName, emit adbParametersChanged(m_androidRunnable.packageName,
AndroidDeviceInfo::adbSelector(info.serialNumber)); AndroidDeviceInfo::adbSelector(info.serialNumber));
if (info.isValid()) { if (info.isValid()) {
if (AndroidConfigurations::currentConfig().findAvd(info.avdname).isEmpty()) { AndroidAvdManager avdManager;
bool launched = AndroidConfigurations::currentConfig().startAVDAsync(info.avdname); if (avdManager.findAvd(info.avdname).isEmpty()) {
bool launched = avdManager.startAvdAsync(info.avdname);
m_launchedAVDName = launched ? info.avdname:""; m_launchedAVDName = launched ? info.avdname:"";
} else { } else {
m_launchedAVDName.clear(); m_launchedAVDName.clear();
@@ -803,11 +805,12 @@ void AndroidRunner::launchAVD()
void AndroidRunner::checkAVD() void AndroidRunner::checkAVD()
{ {
const AndroidConfig &config = AndroidConfigurations::currentConfig(); const AndroidConfig &config = AndroidConfigurations::currentConfig();
QString serialNumber = config.findAvd(m_launchedAVDName); AndroidAvdManager avdManager(config);
QString serialNumber = avdManager.findAvd(m_launchedAVDName);
if (!serialNumber.isEmpty()) if (!serialNumber.isEmpty())
return; // try again on next timer hit return; // try again on next timer hit
if (config.hasFinishedBooting(serialNumber)) { if (avdManager.isAvdBooted(serialNumber)) {
m_checkAVDTimer.stop(); m_checkAVDTimer.stop();
AndroidManager::setDeviceSerialNumber(m_runConfig->target(), serialNumber); AndroidManager::setDeviceSerialNumber(m_runConfig->target(), serialNumber);
emit asyncStart(m_androidRunnable.intentName, m_androidRunnable.beforeStartADBCommands); emit asyncStart(m_androidRunnable.intentName, m_androidRunnable.beforeStartADBCommands);

View File

@@ -30,7 +30,7 @@
#include "androidconfigurations.h" #include "androidconfigurations.h"
#include "androidconstants.h" #include "androidconstants.h"
#include "androidtoolchain.h" #include "androidtoolchain.h"
#include "androidtoolmanager.h" #include "androidavdmanager.h"
#include <utils/environment.h> #include <utils/environment.h>
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
@@ -59,7 +59,7 @@
namespace Android { namespace Android {
namespace Internal { namespace Internal {
void AvdModel::setAvdList(const QVector<AndroidDeviceInfo> &list) void AvdModel::setAvdList(const AndroidDeviceInfoList &list)
{ {
beginResetModel(); beginResetModel();
m_list = list; m_list = list;
@@ -130,7 +130,7 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
m_javaState(NotSet), m_javaState(NotSet),
m_ui(new Ui_AndroidSettingsWidget), m_ui(new Ui_AndroidSettingsWidget),
m_androidConfig(AndroidConfigurations::currentConfig()), m_androidConfig(AndroidConfigurations::currentConfig()),
m_androidToolManager(new AndroidToolManager(m_androidConfig)) m_avdManager(new AndroidAvdManager(m_androidConfig))
{ {
m_ui->setupUi(this); m_ui->setupUi(this);
@@ -465,7 +465,7 @@ void AndroidSettingsWidget::enableAvdControls()
void AndroidSettingsWidget::startUpdateAvd() void AndroidSettingsWidget::startUpdateAvd()
{ {
disableAvdControls(); disableAvdControls();
m_virtualDevicesWatcher.setFuture(m_androidToolManager->androidVirtualDevicesFuture()); m_virtualDevicesWatcher.setFuture(m_avdManager->avdList());
} }
void AndroidSettingsWidget::updateAvds() void AndroidSettingsWidget::updateAvds()
@@ -589,12 +589,12 @@ void AndroidSettingsWidget::addAVD()
disableAvdControls(); disableAvdControls();
AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this); AndroidConfig::CreateAvdInfo info = m_androidConfig.gatherCreateAVDInfo(this);
if (info.target.isEmpty()) { if (!info.target.isValid()) {
enableAvdControls(); enableAvdControls();
return; return;
} }
m_futureWatcher.setFuture(m_androidToolManager->createAvd(info)); m_futureWatcher.setFuture(m_avdManager->createAvd(info));
} }
void AndroidSettingsWidget::avdAdded() void AndroidSettingsWidget::avdAdded()
@@ -622,13 +622,13 @@ void AndroidSettingsWidget::removeAVD()
return; return;
} }
m_androidToolManager->removeAvd(avdName); m_avdManager->removeAvd(avdName);
startUpdateAvd(); startUpdateAvd();
} }
void AndroidSettingsWidget::startAVD() void AndroidSettingsWidget::startAVD()
{ {
m_androidConfig.startAVDAsync(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex())); m_avdManager->startAvdAsync(m_AVDModel.avdName(m_ui->AVDTableView->currentIndex()));
} }
void AndroidSettingsWidget::avdActivated(const QModelIndex &index) void AndroidSettingsWidget::avdActivated(const QModelIndex &index)
@@ -673,7 +673,15 @@ void AndroidSettingsWidget::showGdbWarningDialog()
void AndroidSettingsWidget::manageAVD() void AndroidSettingsWidget::manageAVD()
{ {
m_androidToolManager->launchAvdManager(); if (m_avdManager->avdManagerUiToolAvailable()) {
m_avdManager->launchAvdManagerUiTool();
} else {
QMessageBox::warning(this, tr("AVD Manager Not Available"),
tr("AVD manager UI tool is not available in the installed SDK tools"
"(version %1). Use the command line tool \"avdmanager\" for "
"advanced AVD management.")
.arg(m_androidConfig.sdkToolsVersion().toString()));
}
} }

View File

@@ -42,13 +42,13 @@ QT_END_NAMESPACE
namespace Android { namespace Android {
namespace Internal { namespace Internal {
class AndroidToolManager; class AndroidAvdManager;
class AvdModel: public QAbstractTableModel class AvdModel: public QAbstractTableModel
{ {
Q_OBJECT Q_OBJECT
public: public:
void setAvdList(const QVector<AndroidDeviceInfo> &list); void setAvdList(const AndroidDeviceInfoList &list);
QString avdName(const QModelIndex &index) const; QString avdName(const QModelIndex &index) const;
QModelIndex indexForAvdName(const QString &avdName) const; QModelIndex indexForAvdName(const QString &avdName) const;
@@ -59,7 +59,7 @@ protected:
int columnCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const;
private: private:
QVector<AndroidDeviceInfo> m_list; AndroidDeviceInfoList m_list;
}; };
class AndroidSettingsWidget : public QWidget class AndroidSettingsWidget : public QWidget
@@ -116,14 +116,14 @@ private:
Ui_AndroidSettingsWidget *m_ui; Ui_AndroidSettingsWidget *m_ui;
AndroidConfig m_androidConfig; AndroidConfig m_androidConfig;
std::unique_ptr<AndroidToolManager> m_androidToolManager;
AvdModel m_AVDModel; AvdModel m_AVDModel;
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcher; QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcher;
QFutureWatcher<QPair<QStringList, bool>> m_checkGdbWatcher; QFutureWatcher<QPair<QStringList, bool>> m_checkGdbWatcher;
QStringList m_gdbCheckPaths; QStringList m_gdbCheckPaths;
QFutureWatcher<QVector<AndroidDeviceInfo>> m_virtualDevicesWatcher; QFutureWatcher<AndroidDeviceInfoList> m_virtualDevicesWatcher;
QString m_lastAddedAvd; QString m_lastAddedAvd;
std::unique_ptr<AndroidAvdManager> m_avdManager;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -133,7 +133,7 @@ bool AndroidToolManager::removeAvd(const QString &name) const
return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0; return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0;
} }
QFuture<QVector<AndroidDeviceInfo>> AndroidToolManager::androidVirtualDevicesFuture() const QFuture<AndroidDeviceInfoList> AndroidToolManager::androidVirtualDevicesFuture() const
{ {
return Utils::runAsync(&AndroidToolManager::androidVirtualDevices, return Utils::runAsync(&AndroidToolManager::androidVirtualDevices,
m_config.androidToolPath(), m_config.sdkLocation(), m_config.androidToolPath(), m_config.sdkLocation(),
@@ -161,7 +161,7 @@ AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::Cr
proc.setProcessEnvironment(env.toProcessEnvironment()); proc.setProcessEnvironment(env.toProcessEnvironment());
QStringList arguments; QStringList arguments;
arguments << QLatin1String("create") << QLatin1String("avd") arguments << QLatin1String("create") << QLatin1String("avd")
<< QLatin1String("-t") << info.target << QLatin1String("-t") << info.target.name
<< QLatin1String("-n") << info.name << QLatin1String("-n") << info.name
<< QLatin1String("-b") << info.abi; << QLatin1String("-b") << info.abi;
if (info.sdcardSize > 0) if (info.sdcardSize > 0)
@@ -206,12 +206,11 @@ AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::Cr
return info; return info;
} }
QVector<AndroidDeviceInfo> AndroidDeviceInfoList AndroidToolManager::androidVirtualDevices(const Utils::FileName &androidTool,
AndroidToolManager::androidVirtualDevices(const Utils::FileName &androidTool,
const FileName &sdkLocationPath, const FileName &sdkLocationPath,
const Environment &environment) const Environment &environment)
{ {
QVector<AndroidDeviceInfo> devices; AndroidDeviceInfoList devices;
QString output; QString output;
if (!androidToolCommand(androidTool, QStringList({"list", "avd"}), environment, &output)) if (!androidToolCommand(androidTool, QStringList({"list", "avd"}), environment, &output))
return devices; return devices;

View File

@@ -53,14 +53,14 @@ public:
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const; QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
bool removeAvd(const QString &name) const; bool removeAvd(const QString &name) const;
QFuture<QVector<AndroidDeviceInfo> > androidVirtualDevicesFuture() const; QFuture<AndroidDeviceInfoList> androidVirtualDevicesFuture() const;
// Helper methods // Helper methods
private: private:
Utils::Environment androidToolEnvironment() const; Utils::Environment androidToolEnvironment() const;
static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info, static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info,
Utils::FileName androidToolPath, Utils::Environment env); Utils::FileName androidToolPath, Utils::Environment env);
static QVector<AndroidDeviceInfo> androidVirtualDevices(const Utils::FileName &androidTool, static AndroidDeviceInfoList androidVirtualDevices(const Utils::FileName &androidTool,
const Utils::FileName &sdkLlocationPath, const Utils::FileName &sdkLlocationPath,
const Utils::Environment &environment); const Utils::Environment &environment);
private: private:

View File

@@ -68,12 +68,12 @@ AvdDialog::AvdDialog(int minApiLevel, const QString &targetArch, const AndroidCo
bool AvdDialog::isValid() const bool AvdDialog::isValid() const
{ {
return !name().isEmpty() && !target().isEmpty() && !abi().isEmpty(); return !name().isEmpty() && target().isValid() && !abi().isEmpty();
} }
QString AvdDialog::target() const SdkPlatform AvdDialog::target() const
{ {
return m_avdDialog.targetComboBox->currentText(); return m_avdDialog.targetComboBox->currentData().value<SdkPlatform>();
} }
QString AvdDialog::name() const QString AvdDialog::name() const
@@ -93,8 +93,8 @@ int AvdDialog::sdcardSize() const
void AvdDialog::updateApiLevelComboBox() void AvdDialog::updateApiLevelComboBox()
{ {
QList<SdkPlatform> filteredList; SdkPlatformList filteredList;
QList<SdkPlatform> platforms = m_config->sdkTargets(m_minApiLevel); SdkPlatformList platforms = m_config->sdkTargets(m_minApiLevel);
QString selectedAbi = abi(); QString selectedAbi = abi();
auto hasAbi = [selectedAbi](const SystemImage &image) { auto hasAbi = [selectedAbi](const SystemImage &image) {
@@ -106,7 +106,10 @@ void AvdDialog::updateApiLevelComboBox()
}); });
m_avdDialog.targetComboBox->clear(); m_avdDialog.targetComboBox->clear();
m_avdDialog.targetComboBox->addItems(AndroidConfig::apiLevelNamesFor(filteredList)); foreach (const SdkPlatform &platform, filteredList) {
m_avdDialog.targetComboBox->addItem(AndroidConfig::apiLevelNameFor(platform),
QVariant::fromValue<SdkPlatform>(platform));
}
if (platforms.isEmpty()) { if (platforms.isEmpty()) {
m_avdDialog.warningIcon->setVisible(true); m_avdDialog.warningIcon->setVisible(true);

View File

@@ -32,6 +32,7 @@
namespace Android { namespace Android {
class AndroidConfig; class AndroidConfig;
class SdkPlatform;
namespace Internal { namespace Internal {
@@ -42,7 +43,7 @@ public:
explicit AvdDialog(int minApiLevel, const QString &targetArch, explicit AvdDialog(int minApiLevel, const QString &targetArch,
const AndroidConfig *config, QWidget *parent = 0); const AndroidConfig *config, QWidget *parent = 0);
QString target() const; Android::SdkPlatform target() const;
QString name() const; QString name() const;
QString abi() const; QString abi() const;
int sdcardSize() const; int sdcardSize() const;