forked from qt-creator/qt-creator
Android: Add Android tool manager
Refactor the use of android tool and groundwork for the new sdk and avd management tool's integration Task-number: QTCREATORBUG-17814 Change-Id: I6a5920f9ba92508f904cd8cf28bf62c82de2d820 Reviewed-by: BogDan Vatra <bogdan@kdab.com>
This commit is contained in:
@@ -47,7 +47,8 @@ HEADERS += \
|
|||||||
android_global.h \
|
android_global.h \
|
||||||
androidbuildapkstep.h \
|
androidbuildapkstep.h \
|
||||||
androidbuildapkwidget.h \
|
androidbuildapkwidget.h \
|
||||||
androidrunnable.h
|
androidrunnable.h \
|
||||||
|
androidtoolmanager.h
|
||||||
|
|
||||||
SOURCES += \
|
SOURCES += \
|
||||||
androidconfigurations.cpp \
|
androidconfigurations.cpp \
|
||||||
@@ -88,7 +89,8 @@ SOURCES += \
|
|||||||
androidbuildapkstep.cpp \
|
androidbuildapkstep.cpp \
|
||||||
androidbuildapkwidget.cpp \
|
androidbuildapkwidget.cpp \
|
||||||
androidqtsupport.cpp \
|
androidqtsupport.cpp \
|
||||||
androidrunnable.cpp
|
androidrunnable.cpp \
|
||||||
|
androidtoolmanager.cpp
|
||||||
|
|
||||||
FORMS += \
|
FORMS += \
|
||||||
androidsettingswidget.ui \
|
androidsettingswidget.ui \
|
||||||
|
|||||||
@@ -93,6 +93,8 @@ Project {
|
|||||||
"androidsignaloperation.h",
|
"androidsignaloperation.h",
|
||||||
"androidtoolchain.cpp",
|
"androidtoolchain.cpp",
|
||||||
"androidtoolchain.h",
|
"androidtoolchain.h",
|
||||||
|
"androidtoolmanager.cpp",
|
||||||
|
"androidtoolmanager.h",
|
||||||
"avddialog.cpp",
|
"avddialog.cpp",
|
||||||
"avddialog.h",
|
"avddialog.h",
|
||||||
"certificatesmodel.cpp",
|
"certificatesmodel.cpp",
|
||||||
|
|||||||
@@ -28,8 +28,10 @@
|
|||||||
#include "androidtoolchain.h"
|
#include "androidtoolchain.h"
|
||||||
#include "androiddevice.h"
|
#include "androiddevice.h"
|
||||||
#include "androidgdbserverkitinformation.h"
|
#include "androidgdbserverkitinformation.h"
|
||||||
|
#include "androidmanager.h"
|
||||||
#include "androidqtversion.h"
|
#include "androidqtversion.h"
|
||||||
#include "androiddevicedialog.h"
|
#include "androiddevicedialog.h"
|
||||||
|
#include "androidtoolmanager.h"
|
||||||
#include "avddialog.h"
|
#include "avddialog.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
@@ -113,33 +115,6 @@ namespace {
|
|||||||
+ QLatin1String("/qtcreator/android.xml");
|
+ QLatin1String("/qtcreator/android.xml");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool androidDevicesLessThan(const AndroidDeviceInfo &dev1, const AndroidDeviceInfo &dev2)
|
|
||||||
{
|
|
||||||
if (dev1.serialNumber.contains(QLatin1String("????")) != dev2.serialNumber.contains(QLatin1String("????")))
|
|
||||||
return !dev1.serialNumber.contains(QLatin1String("????"));
|
|
||||||
if (dev1.type != dev2.type)
|
|
||||||
return dev1.type == AndroidDeviceInfo::Hardware;
|
|
||||||
if (dev1.sdk != dev2.sdk)
|
|
||||||
return dev1.sdk < dev2.sdk;
|
|
||||||
if (dev1.avdname != dev2.avdname)
|
|
||||||
return dev1.avdname < dev2.avdname;
|
|
||||||
|
|
||||||
return dev1.serialNumber < dev2.serialNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
static QStringList cleanAndroidABIs(const QStringList &abis)
|
|
||||||
{
|
|
||||||
QStringList res;
|
|
||||||
foreach (const QString &abi, abis) {
|
|
||||||
int index = abi.lastIndexOf(QLatin1Char('/'));
|
|
||||||
if (index == -1)
|
|
||||||
res << abi;
|
|
||||||
else
|
|
||||||
res << abi.mid(index + 1);
|
|
||||||
}
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool is32BitUserSpace()
|
static bool is32BitUserSpace()
|
||||||
{
|
{
|
||||||
// Do the exact same check as android's emulator is doing:
|
// Do the exact same check as android's emulator is doing:
|
||||||
@@ -162,25 +137,6 @@ namespace {
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some preview sdks use a non integer version
|
|
||||||
int apiLevelFromAndroidList(const QString &string)
|
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
int result = string.toInt(&ok);
|
|
||||||
if (ok)
|
|
||||||
return result;
|
|
||||||
Utils::FileName sdkLocation = AndroidConfigurations::currentConfig().sdkLocation();
|
|
||||||
sdkLocation.appendPath(QLatin1String("/platforms/android-") + string + QLatin1String("/source.properties"));
|
|
||||||
result = QSettings(sdkLocation.toString(), QSettings::IniFormat).value(QLatin1String("AndroidVersion.ApiLevel")).toInt(&ok);
|
|
||||||
if (ok)
|
|
||||||
return result;
|
|
||||||
if (string == QLatin1String("L"))
|
|
||||||
return 21;
|
|
||||||
if (string == QLatin1String("MNC"))
|
|
||||||
return 22;
|
|
||||||
return 23; // At least
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//////////////////////////////////
|
//////////////////////////////////
|
||||||
@@ -372,48 +328,11 @@ void AndroidConfig::updateAvailableSdkPlatforms() const
|
|||||||
{
|
{
|
||||||
if (m_availableSdkPlatformsUpToDate)
|
if (m_availableSdkPlatformsUpToDate)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
m_availableSdkPlatforms.clear();
|
m_availableSdkPlatforms.clear();
|
||||||
|
AndroidToolManager toolManager(*this);
|
||||||
SynchronousProcess proc;
|
m_availableSdkPlatforms = toolManager.availableSdkPlatforms();
|
||||||
proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
|
Utils::sort(m_availableSdkPlatforms, sortSdkPlatformByApiLevel);
|
||||||
SynchronousProcessResponse response
|
|
||||||
= proc.runBlocking(androidToolPath().toString(),
|
|
||||||
QStringList({"list", "target"})); // list available AVDs
|
|
||||||
if (response.result != SynchronousProcessResponse::Finished)
|
|
||||||
return;
|
|
||||||
|
|
||||||
SdkPlatform platform;
|
|
||||||
foreach (const QString &l, response.allOutput().split('\n')) {
|
|
||||||
const QString line = l.trimmed();
|
|
||||||
if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) {
|
|
||||||
int index = line.indexOf(QLatin1String("\"android-"));
|
|
||||||
if (index == -1)
|
|
||||||
continue;
|
|
||||||
QString androidTarget = line.mid(index + 1, line.length() - index - 2);
|
|
||||||
const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1);
|
|
||||||
platform.apiLevel = apiLevelFromAndroidList(tmp);
|
|
||||||
} else if (line.startsWith(QLatin1String("Name:"))) {
|
|
||||||
platform.name = line.mid(6);
|
|
||||||
} else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
|
|
||||||
platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
|
|
||||||
} else if (line.startsWith(QLatin1String("ABIs"))) {
|
|
||||||
platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
|
|
||||||
} else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) {
|
|
||||||
if (platform.apiLevel == -1)
|
|
||||||
continue;
|
|
||||||
auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
|
|
||||||
platform, sortSdkPlatformByApiLevel);
|
|
||||||
m_availableSdkPlatforms.insert(it, platform);
|
|
||||||
platform = SdkPlatform();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (platform.apiLevel != -1) {
|
|
||||||
auto it = std::lower_bound(m_availableSdkPlatforms.begin(), m_availableSdkPlatforms.end(),
|
|
||||||
platform, sortSdkPlatformByApiLevel);
|
|
||||||
m_availableSdkPlatforms.insert(it, platform);
|
|
||||||
}
|
|
||||||
|
|
||||||
m_availableSdkPlatformsUpToDate = true;
|
m_availableSdkPlatformsUpToDate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -446,18 +365,6 @@ FileName AndroidConfig::adbToolPath() const
|
|||||||
return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
|
return path.appendPath(QLatin1String("platform-tools/adb" QTC_HOST_EXE_SUFFIX));
|
||||||
}
|
}
|
||||||
|
|
||||||
Environment AndroidConfig::androidToolEnvironment() const
|
|
||||||
{
|
|
||||||
Environment env = Environment::systemEnvironment();
|
|
||||||
if (!m_openJDKLocation.isEmpty()) {
|
|
||||||
env.set(QLatin1String("JAVA_HOME"), m_openJDKLocation.toUserOutput());
|
|
||||||
Utils::FileName binPath = m_openJDKLocation;
|
|
||||||
binPath.appendPath(QLatin1String("bin"));
|
|
||||||
env.prependOrSetPath(binPath.toUserOutput());
|
|
||||||
}
|
|
||||||
return env;
|
|
||||||
}
|
|
||||||
|
|
||||||
FileName AndroidConfig::androidToolPath() const
|
FileName AndroidConfig::androidToolPath() const
|
||||||
{
|
{
|
||||||
if (HostOsInfo::isWindowsHost()) {
|
if (HostOsInfo::isWindowsHost()) {
|
||||||
@@ -583,7 +490,7 @@ QVector<AndroidDeviceInfo> AndroidConfig::connectedDevices(const QString &adbToo
|
|||||||
devices.push_back(dev);
|
devices.push_back(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
Utils::sort(devices, androidDevicesLessThan);
|
Utils::sort(devices);
|
||||||
if (devices.isEmpty() && error)
|
if (devices.isEmpty() && error)
|
||||||
*error = QApplication::translate("AndroidConfiguration",
|
*error = QApplication::translate("AndroidConfiguration",
|
||||||
"No devices found in output of: %1")
|
"No devices found in output of: %1")
|
||||||
@@ -605,157 +512,6 @@ AndroidConfig::CreateAvdInfo AndroidConfig::gatherCreateAVDInfo(QWidget *parent,
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
QFuture<AndroidConfig::CreateAvdInfo> AndroidConfig::createAVD(CreateAvdInfo info) const
|
|
||||||
{
|
|
||||||
return Utils::runAsync(&AndroidConfig::createAVDImpl, info,
|
|
||||||
androidToolPath(), androidToolEnvironment());
|
|
||||||
}
|
|
||||||
|
|
||||||
AndroidConfig::CreateAvdInfo AndroidConfig::createAVDImpl(CreateAvdInfo info, FileName androidToolPath, Environment env)
|
|
||||||
{
|
|
||||||
QProcess proc;
|
|
||||||
proc.setProcessEnvironment(env.toProcessEnvironment());
|
|
||||||
QStringList arguments;
|
|
||||||
arguments << QLatin1String("create") << QLatin1String("avd")
|
|
||||||
<< QLatin1String("-t") << info.target
|
|
||||||
<< QLatin1String("-n") << info.name
|
|
||||||
<< QLatin1String("-b") << info.abi;
|
|
||||||
if (info.sdcardSize > 0)
|
|
||||||
arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize);
|
|
||||||
proc.start(androidToolPath.toString(), arguments);
|
|
||||||
if (!proc.waitForStarted()) {
|
|
||||||
info.error = QApplication::translate("AndroidConfig", "Could not start process \"%1 %2\"")
|
|
||||||
.arg(androidToolPath.toString(), arguments.join(QLatin1Char(' ')));
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
QTC_CHECK(proc.state() == QProcess::Running);
|
|
||||||
proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"
|
|
||||||
|
|
||||||
QByteArray question;
|
|
||||||
while (true) {
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (proc.state() != QProcess::Running)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
QTC_CHECK(proc.state() == QProcess::NotRunning);
|
|
||||||
|
|
||||||
QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
|
|
||||||
// The exit code is always 0, so we need to check stderr
|
|
||||||
// For now assume that any output at all indicates a error
|
|
||||||
if (!errorOutput.isEmpty()) {
|
|
||||||
info.error = errorOutput;
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AndroidConfig::removeAVD(const QString &name) const
|
|
||||||
{
|
|
||||||
SynchronousProcess proc;
|
|
||||||
proc.setTimeoutS(5);
|
|
||||||
proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
|
|
||||||
SynchronousProcessResponse response
|
|
||||||
= proc.runBlocking(androidToolPath().toString(), QStringList({"delete", "avd", "-n", name}));
|
|
||||||
return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
QFuture<QVector<AndroidDeviceInfo>> AndroidConfig::androidVirtualDevicesFuture() const
|
|
||||||
{
|
|
||||||
return Utils::runAsync(&AndroidConfig::androidVirtualDevices,
|
|
||||||
androidToolPath().toString(), androidToolEnvironment());
|
|
||||||
}
|
|
||||||
|
|
||||||
QVector<AndroidDeviceInfo> AndroidConfig::androidVirtualDevices(const QString &androidTool, const Environment &environment)
|
|
||||||
{
|
|
||||||
QVector<AndroidDeviceInfo> devices;
|
|
||||||
SynchronousProcess proc;
|
|
||||||
proc.setTimeoutS(20);
|
|
||||||
proc.setProcessEnvironment(environment.toProcessEnvironment());
|
|
||||||
SynchronousProcessResponse response = proc.run(androidTool, {"list", "avd"}); // list available AVDs
|
|
||||||
if (response.result != SynchronousProcessResponse::Finished)
|
|
||||||
return devices;
|
|
||||||
|
|
||||||
QStringList avds = response.allOutput().split('\n');
|
|
||||||
if (avds.empty())
|
|
||||||
return devices;
|
|
||||||
|
|
||||||
while (avds.first().startsWith(QLatin1String("* daemon")))
|
|
||||||
avds.removeFirst(); // remove the daemon logs
|
|
||||||
avds.removeFirst(); // remove "List of devices attached" header line
|
|
||||||
|
|
||||||
bool nextLineIsTargetLine = false;
|
|
||||||
|
|
||||||
AndroidDeviceInfo dev;
|
|
||||||
for (int i = 0; i < avds.size(); i++) {
|
|
||||||
QString line = avds.at(i);
|
|
||||||
if (!line.contains(QLatin1String("Name:")))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int index = line.indexOf(QLatin1Char(':')) + 2;
|
|
||||||
if (index >= line.size())
|
|
||||||
break;
|
|
||||||
dev.avdname = line.mid(index).trimmed();
|
|
||||||
dev.sdk = -1;
|
|
||||||
dev.cpuAbi.clear();
|
|
||||||
++i;
|
|
||||||
for (; i < avds.size(); ++i) {
|
|
||||||
line = avds.at(i);
|
|
||||||
if (line.contains(QLatin1String("---------")))
|
|
||||||
break;
|
|
||||||
|
|
||||||
if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) {
|
|
||||||
if (line.contains(QLatin1String("Google APIs"))) {
|
|
||||||
nextLineIsTargetLine = true;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
nextLineIsTargetLine = false;
|
|
||||||
|
|
||||||
int lastIndex = line.lastIndexOf(QLatin1Char(' '));
|
|
||||||
if (lastIndex == -1) // skip line
|
|
||||||
break;
|
|
||||||
QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed();
|
|
||||||
dev.sdk = apiLevelFromAndroidList(tmp);
|
|
||||||
}
|
|
||||||
if (line.contains(QLatin1String("Tag/ABI:"))) {
|
|
||||||
int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1;
|
|
||||||
if (lastIndex >= line.size())
|
|
||||||
break;
|
|
||||||
dev.cpuAbi = QStringList(line.mid(lastIndex));
|
|
||||||
} else if (line.contains(QLatin1String("ABI:"))) {
|
|
||||||
int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1;
|
|
||||||
if (lastIndex >= line.size())
|
|
||||||
break;
|
|
||||||
dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// armeabi-v7a devices can also run armeabi code
|
|
||||||
if (dev.cpuAbi == QStringList("armeabi-v7a"))
|
|
||||||
dev.cpuAbi << QLatin1String("armeabi");
|
|
||||||
dev.state = AndroidDeviceInfo::OkState;
|
|
||||||
dev.type = AndroidDeviceInfo::Emulator;
|
|
||||||
if (dev.cpuAbi.isEmpty() || dev.sdk == -1)
|
|
||||||
continue;
|
|
||||||
devices.push_back(dev);
|
|
||||||
}
|
|
||||||
Utils::sort(devices, androidDevicesLessThan);
|
|
||||||
|
|
||||||
return devices;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString AndroidConfig::startAVD(const QString &name) const
|
QString AndroidConfig::startAVD(const QString &name) const
|
||||||
{
|
{
|
||||||
if (!findAvd(name).isEmpty() || startAVDAsync(name))
|
if (!findAvd(name).isEmpty() || startAVDAsync(name))
|
||||||
@@ -1353,6 +1109,20 @@ QStringList AndroidDeviceInfo::adbSelector(const QString &serialNumber)
|
|||||||
return QStringList({"-s", serialNumber});
|
return QStringList({"-s", serialNumber});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AndroidDeviceInfo::operator<(const AndroidDeviceInfo &other) const
|
||||||
|
{
|
||||||
|
if (serialNumber.contains("????") != other.serialNumber.contains("????"))
|
||||||
|
return !serialNumber.contains("????");
|
||||||
|
if (type != other.type)
|
||||||
|
return type == AndroidDeviceInfo::Hardware;
|
||||||
|
if (sdk != other.sdk)
|
||||||
|
return sdk < other.sdk;
|
||||||
|
if (avdname != other.avdname)
|
||||||
|
return avdname < other.avdname;
|
||||||
|
|
||||||
|
return serialNumber < other.serialNumber;
|
||||||
|
}
|
||||||
|
|
||||||
const AndroidConfig &AndroidConfigurations::currentConfig()
|
const AndroidConfig &AndroidConfigurations::currentConfig()
|
||||||
{
|
{
|
||||||
return m_instance->m_config; // ensure that m_instance is initialized
|
return m_instance->m_config; // ensure that m_instance is initialized
|
||||||
|
|||||||
@@ -68,19 +68,19 @@ public:
|
|||||||
|
|
||||||
static QStringList adbSelector(const QString &serialNumber);
|
static QStringList adbSelector(const QString &serialNumber);
|
||||||
|
|
||||||
bool isValid() { return !serialNumber.isEmpty() || !avdname.isEmpty(); }
|
bool isValid() const { return !serialNumber.isEmpty() || !avdname.isEmpty(); }
|
||||||
|
bool operator<(const AndroidDeviceInfo &other) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
class SdkPlatform
|
class SdkPlatform
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SdkPlatform()
|
int apiLevel = -1;
|
||||||
: apiLevel(-1)
|
|
||||||
{}
|
|
||||||
int apiLevel;
|
|
||||||
QString name;
|
QString name;
|
||||||
|
Utils::FileName installedLocation;
|
||||||
QStringList abis;
|
QStringList abis;
|
||||||
};
|
};
|
||||||
|
using SdkPlatformList = QList<SdkPlatform>;
|
||||||
|
|
||||||
class ANDROID_EXPORT AndroidConfig
|
class ANDROID_EXPORT AndroidConfig
|
||||||
{
|
{
|
||||||
@@ -121,13 +121,12 @@ public:
|
|||||||
|
|
||||||
Utils::FileName adbToolPath() const;
|
Utils::FileName adbToolPath() const;
|
||||||
Utils::FileName androidToolPath() const;
|
Utils::FileName androidToolPath() const;
|
||||||
Utils::Environment androidToolEnvironment() const;
|
|
||||||
Utils::FileName antToolPath() const;
|
Utils::FileName antToolPath() const;
|
||||||
Utils::FileName emulatorToolPath() const;
|
Utils::FileName emulatorToolPath() 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;
|
||||||
|
|
||||||
Utils::FileName gdbPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
|
Utils::FileName gdbPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
|
||||||
|
|
||||||
Utils::FileName keytoolPath() const;
|
Utils::FileName keytoolPath() const;
|
||||||
@@ -143,15 +142,10 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, int minApiLevel = 0, QString targetArch = QString()) const;
|
CreateAvdInfo gatherCreateAVDInfo(QWidget *parent, int minApiLevel = 0, QString targetArch = QString()) const;
|
||||||
QFuture<CreateAvdInfo> createAVD(CreateAvdInfo info) const;
|
|
||||||
bool removeAVD(const QString &name) const;
|
|
||||||
|
|
||||||
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);
|
||||||
|
|
||||||
QFuture<QVector<AndroidDeviceInfo> > androidVirtualDevicesFuture() const;
|
|
||||||
static QVector<AndroidDeviceInfo> androidVirtualDevices(const QString &androidTool, const Utils::Environment &environment);
|
|
||||||
|
|
||||||
QString startAVD(const QString &name) const;
|
QString startAVD(const QString &name) const;
|
||||||
bool startAVDAsync(const QString &avdName) const;
|
bool startAVDAsync(const QString &avdName) const;
|
||||||
QString findAvd(const QString &avdName) const;
|
QString findAvd(const QString &avdName) const;
|
||||||
@@ -172,7 +166,6 @@ public:
|
|||||||
|
|
||||||
SdkPlatform highestAndroidSdk() const;
|
SdkPlatform highestAndroidSdk() const;
|
||||||
private:
|
private:
|
||||||
static CreateAvdInfo createAVDImpl(CreateAvdInfo info, Utils::FileName androidToolPath, Utils::Environment env);
|
|
||||||
static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property);
|
static QString getDeviceProperty(const QString &adbToolPath, const QString &device, const QString &property);
|
||||||
|
|
||||||
Utils::FileName toolPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
|
Utils::FileName toolPath(const ProjectExplorer::Abi &abi, const QString &ndkToolChainVersion) const;
|
||||||
@@ -200,7 +193,7 @@ private:
|
|||||||
|
|
||||||
//caches
|
//caches
|
||||||
mutable bool m_availableSdkPlatformsUpToDate = false;
|
mutable bool m_availableSdkPlatformsUpToDate = false;
|
||||||
mutable QVector<SdkPlatform> m_availableSdkPlatforms;
|
mutable SdkPlatformList m_availableSdkPlatforms;
|
||||||
static bool sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b);
|
static bool sortSdkPlatformByApiLevel(const SdkPlatform &a, const SdkPlatform &b);
|
||||||
|
|
||||||
mutable bool m_NdkInformationUpToDate = false;
|
mutable bool m_NdkInformationUpToDate = false;
|
||||||
|
|||||||
@@ -423,7 +423,8 @@ AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi, Andro
|
|||||||
m_ui(new Ui::AndroidDeviceDialog),
|
m_ui(new Ui::AndroidDeviceDialog),
|
||||||
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_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
m_ui->deviceView->setModel(m_model);
|
m_ui->deviceView->setModel(m_model);
|
||||||
@@ -515,7 +516,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(AndroidConfigurations::currentConfig().androidVirtualDevicesFuture());
|
m_futureWatcherRefreshDevices.setFuture(m_androidToolManager->androidVirtualDevicesFuture());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceDialog::devicesRefreshed()
|
void AndroidDeviceDialog::devicesRefreshed()
|
||||||
@@ -588,7 +589,7 @@ void AndroidDeviceDialog::createAvd()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_futureWatcherAddDevice.setFuture(AndroidConfigurations::currentConfig().createAVD(info));
|
m_futureWatcherAddDevice.setFuture(m_androidToolManager->createAvd(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidDeviceDialog::avdAdded()
|
void AndroidDeviceDialog::avdAdded()
|
||||||
|
|||||||
@@ -26,12 +26,15 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
|
#include "androidtoolmanager.h"
|
||||||
|
|
||||||
#include <QVector>
|
#include <QVector>
|
||||||
#include <QDialog>
|
#include <QDialog>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
#include <QTime>
|
#include <QTime>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QModelIndex;
|
class QModelIndex;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
@@ -74,6 +77,7 @@ 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;
|
||||||
QVector<AndroidDeviceInfo> m_connectedDevices;
|
QVector<AndroidDeviceInfo> m_connectedDevices;
|
||||||
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
|
QFutureWatcher<AndroidConfig::CreateAvdInfo> m_futureWatcherAddDevice;
|
||||||
QFutureWatcher<QVector<AndroidDeviceInfo>> m_futureWatcherRefreshDevices;
|
QFutureWatcher<QVector<AndroidDeviceInfo>> m_futureWatcherRefreshDevices;
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ namespace {
|
|||||||
const QLatin1String AndroidManifestName("AndroidManifest.xml");
|
const QLatin1String AndroidManifestName("AndroidManifest.xml");
|
||||||
const QLatin1String AndroidDefaultPropertiesName("project.properties");
|
const QLatin1String AndroidDefaultPropertiesName("project.properties");
|
||||||
const QLatin1String AndroidDeviceSn("AndroidDeviceSerialNumber");
|
const QLatin1String AndroidDeviceSn("AndroidDeviceSerialNumber");
|
||||||
|
const QLatin1String ApiLevelKey("AndroidVersion.ApiLevel");
|
||||||
|
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
@@ -580,4 +581,19 @@ bool AndroidManager::updateGradleProperties(ProjectExplorer::Target *target)
|
|||||||
return mergeGradleProperties(gradlePropertiesPath, gradleProperties);
|
return mergeGradleProperties(gradlePropertiesPath, gradleProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int AndroidManager::findApiLevel(const Utils::FileName &platformPath)
|
||||||
|
{
|
||||||
|
int apiLevel = -1;
|
||||||
|
Utils::FileName propertiesPath = platformPath;
|
||||||
|
propertiesPath.appendPath("/source.properties");
|
||||||
|
if (propertiesPath.exists()) {
|
||||||
|
QSettings sdkProperties(propertiesPath.toString(), QSettings::IniFormat);
|
||||||
|
bool validInt = false;
|
||||||
|
apiLevel = sdkProperties.value(ApiLevelKey).toInt(&validInt);
|
||||||
|
if (!validInt)
|
||||||
|
apiLevel = -1;
|
||||||
|
}
|
||||||
|
return apiLevel;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Android
|
} // namespace Android
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ public:
|
|||||||
static AndroidQtSupport *androidQtSupport(ProjectExplorer::Target *target);
|
static AndroidQtSupport *androidQtSupport(ProjectExplorer::Target *target);
|
||||||
static bool useGradle(ProjectExplorer::Target *target);
|
static bool useGradle(ProjectExplorer::Target *target);
|
||||||
static bool updateGradleProperties(ProjectExplorer::Target *target);
|
static bool updateGradleProperties(ProjectExplorer::Target *target);
|
||||||
|
static int findApiLevel(const Utils::FileName &platformPath);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Android
|
} // namespace Android
|
||||||
|
|||||||
@@ -30,6 +30,7 @@
|
|||||||
#include "androidconfigurations.h"
|
#include "androidconfigurations.h"
|
||||||
#include "androidconstants.h"
|
#include "androidconstants.h"
|
||||||
#include "androidtoolchain.h"
|
#include "androidtoolchain.h"
|
||||||
|
#include "androidtoolmanager.h"
|
||||||
|
|
||||||
#include <utils/environment.h>
|
#include <utils/environment.h>
|
||||||
#include <utils/hostosinfo.h>
|
#include <utils/hostosinfo.h>
|
||||||
@@ -128,7 +129,8 @@ AndroidSettingsWidget::AndroidSettingsWidget(QWidget *parent)
|
|||||||
m_ndkState(NotSet),
|
m_ndkState(NotSet),
|
||||||
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_ui->setupUi(this);
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
@@ -463,7 +465,7 @@ void AndroidSettingsWidget::enableAvdControls()
|
|||||||
void AndroidSettingsWidget::startUpdateAvd()
|
void AndroidSettingsWidget::startUpdateAvd()
|
||||||
{
|
{
|
||||||
disableAvdControls();
|
disableAvdControls();
|
||||||
m_virtualDevicesWatcher.setFuture(m_androidConfig.androidVirtualDevicesFuture());
|
m_virtualDevicesWatcher.setFuture(m_androidToolManager->androidVirtualDevicesFuture());
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::updateAvds()
|
void AndroidSettingsWidget::updateAvds()
|
||||||
@@ -592,7 +594,7 @@ void AndroidSettingsWidget::addAVD()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_futureWatcher.setFuture(m_androidConfig.createAVD(info));
|
m_futureWatcher.setFuture(m_androidToolManager->createAvd(info));
|
||||||
}
|
}
|
||||||
|
|
||||||
void AndroidSettingsWidget::avdAdded()
|
void AndroidSettingsWidget::avdAdded()
|
||||||
@@ -620,7 +622,7 @@ void AndroidSettingsWidget::removeAVD()
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_androidConfig.removeAVD(avdName);
|
m_androidToolManager->removeAvd(avdName);
|
||||||
startUpdateAvd();
|
startUpdateAvd();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,16 +673,7 @@ void AndroidSettingsWidget::showGdbWarningDialog()
|
|||||||
|
|
||||||
void AndroidSettingsWidget::manageAVD()
|
void AndroidSettingsWidget::manageAVD()
|
||||||
{
|
{
|
||||||
QProcess *avdProcess = new QProcess();
|
m_androidToolManager->launchAvdManager();
|
||||||
connect(this, &QObject::destroyed, avdProcess, &QObject::deleteLater);
|
|
||||||
connect(avdProcess, static_cast<void (QProcess::*)(int)>(&QProcess::finished),
|
|
||||||
avdProcess, &QObject::deleteLater);
|
|
||||||
|
|
||||||
avdProcess->setProcessEnvironment(m_androidConfig.androidToolEnvironment().toProcessEnvironment());
|
|
||||||
QString executable = m_androidConfig.androidToolPath().toString();
|
|
||||||
QStringList arguments = QStringList("avd");
|
|
||||||
|
|
||||||
avdProcess->start(executable, arguments);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@
|
|||||||
#include <QAbstractTableModel>
|
#include <QAbstractTableModel>
|
||||||
#include <QFutureWatcher>
|
#include <QFutureWatcher>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class Ui_AndroidSettingsWidget;
|
class Ui_AndroidSettingsWidget;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
@@ -40,6 +42,8 @@ QT_END_NAMESPACE
|
|||||||
namespace Android {
|
namespace Android {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
class AndroidToolManager;
|
||||||
|
|
||||||
class AvdModel: public QAbstractTableModel
|
class AvdModel: public QAbstractTableModel
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
@@ -112,6 +116,7 @@ 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;
|
||||||
|
|||||||
329
src/plugins/android/androidtoolmanager.cpp
Normal file
329
src/plugins/android/androidtoolmanager.cpp
Normal file
@@ -0,0 +1,329 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "androidtoolmanager.h"
|
||||||
|
|
||||||
|
#include "androidmanager.h"
|
||||||
|
|
||||||
|
#include "utils/algorithm.h"
|
||||||
|
#include "utils/environment.h"
|
||||||
|
#include "utils/qtcassert.h"
|
||||||
|
#include "utils/runextensions.h"
|
||||||
|
#include "utils/synchronousprocess.h"
|
||||||
|
|
||||||
|
#include <QLoggingCategory>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
Q_LOGGING_CATEGORY(androidToolLog, "qtc.android.sdkManager")
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
using namespace Utils;
|
||||||
|
|
||||||
|
class AndroidToolOutputParser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
void parseTargetListing(const QString &output, const FileName &sdkLocation,
|
||||||
|
SdkPlatformList *platformList);
|
||||||
|
|
||||||
|
QList<SdkPlatform> m_installedPlatforms;
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Runs the \c android tool located at \a toolPath with arguments \a args and environment \a
|
||||||
|
environment. Returns \c true for successful execution. Command's output is copied to \a
|
||||||
|
output.
|
||||||
|
*/
|
||||||
|
static bool androidToolCommand(Utils::FileName toolPath, const QStringList &args,
|
||||||
|
const Environment &environment, QString *output)
|
||||||
|
{
|
||||||
|
QString androidToolPath = toolPath.toString();
|
||||||
|
SynchronousProcess proc;
|
||||||
|
proc.setProcessEnvironment(environment.toProcessEnvironment());
|
||||||
|
SynchronousProcessResponse response = proc.runBlocking(androidToolPath, args);
|
||||||
|
if (response.result == SynchronousProcessResponse::Finished) {
|
||||||
|
if (output)
|
||||||
|
*output = response.allOutput();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static QStringList cleanAndroidABIs(const QStringList &abis)
|
||||||
|
{
|
||||||
|
QStringList res;
|
||||||
|
foreach (const QString &abi, abis) {
|
||||||
|
int index = abi.lastIndexOf(QLatin1Char('/'));
|
||||||
|
if (index == -1)
|
||||||
|
res << abi;
|
||||||
|
else
|
||||||
|
res << abi.mid(index + 1);
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidToolManager::AndroidToolManager(const AndroidConfig &config) :
|
||||||
|
m_config(config),
|
||||||
|
m_parser(new AndroidToolOutputParser)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidToolManager::~AndroidToolManager()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
SdkPlatformList AndroidToolManager::availableSdkPlatforms() const
|
||||||
|
{
|
||||||
|
SdkPlatformList list;
|
||||||
|
QString targetListing;
|
||||||
|
if (androidToolCommand(m_config.androidToolPath(), QStringList({"list", "target"}),
|
||||||
|
androidToolEnvironment(), &targetListing)) {
|
||||||
|
m_parser->parseTargetListing(targetListing, m_config.sdkLocation(), &list);
|
||||||
|
} else {
|
||||||
|
qCDebug(androidToolLog) << "Android tool target listing failed";
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidToolManager::launchAvdManager() const
|
||||||
|
{
|
||||||
|
QProcess::startDetached(m_config.androidToolPath().toString(), QStringList("avd"));
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<AndroidConfig::CreateAvdInfo>
|
||||||
|
AndroidToolManager::createAvd(AndroidConfig::CreateAvdInfo info) const
|
||||||
|
{
|
||||||
|
return Utils::runAsync(&AndroidToolManager::createAvdImpl, info,
|
||||||
|
m_config.androidToolPath(), androidToolEnvironment());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AndroidToolManager::removeAvd(const QString &name) const
|
||||||
|
{
|
||||||
|
SynchronousProcess proc;
|
||||||
|
proc.setTimeoutS(5);
|
||||||
|
proc.setProcessEnvironment(androidToolEnvironment().toProcessEnvironment());
|
||||||
|
SynchronousProcessResponse response
|
||||||
|
= proc.runBlocking(m_config.androidToolPath().toString(),
|
||||||
|
QStringList({"delete", "avd", "-n", name}));
|
||||||
|
return response.result == SynchronousProcessResponse::Finished && response.exitCode == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFuture<QVector<AndroidDeviceInfo>> AndroidToolManager::androidVirtualDevicesFuture() const
|
||||||
|
{
|
||||||
|
return Utils::runAsync(&AndroidToolManager::androidVirtualDevices,
|
||||||
|
m_config.androidToolPath(), m_config.sdkLocation(),
|
||||||
|
androidToolEnvironment());
|
||||||
|
}
|
||||||
|
|
||||||
|
Environment AndroidToolManager::androidToolEnvironment() const
|
||||||
|
{
|
||||||
|
Environment env = Environment::systemEnvironment();
|
||||||
|
Utils::FileName jdkLocation = m_config.openJDKLocation();
|
||||||
|
if (!jdkLocation.isEmpty()) {
|
||||||
|
env.set(QLatin1String("JAVA_HOME"), jdkLocation.toUserOutput());
|
||||||
|
Utils::FileName binPath = jdkLocation;
|
||||||
|
binPath.appendPath(QLatin1String("bin"));
|
||||||
|
env.prependOrSetPath(binPath.toUserOutput());
|
||||||
|
}
|
||||||
|
return env;
|
||||||
|
}
|
||||||
|
|
||||||
|
AndroidConfig::CreateAvdInfo AndroidToolManager::createAvdImpl(AndroidConfig::CreateAvdInfo info,
|
||||||
|
FileName androidToolPath,
|
||||||
|
Environment env)
|
||||||
|
{
|
||||||
|
QProcess proc;
|
||||||
|
proc.setProcessEnvironment(env.toProcessEnvironment());
|
||||||
|
QStringList arguments;
|
||||||
|
arguments << QLatin1String("create") << QLatin1String("avd")
|
||||||
|
<< QLatin1String("-t") << info.target
|
||||||
|
<< QLatin1String("-n") << info.name
|
||||||
|
<< QLatin1String("-b") << info.abi;
|
||||||
|
if (info.sdcardSize > 0)
|
||||||
|
arguments << QLatin1String("-c") << QString::fromLatin1("%1M").arg(info.sdcardSize);
|
||||||
|
proc.start(androidToolPath.toString(), arguments);
|
||||||
|
if (!proc.waitForStarted()) {
|
||||||
|
info.error = tr("Could not start process \"%1 %2\"")
|
||||||
|
.arg(androidToolPath.toString(), arguments.join(QLatin1Char(' ')));
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
QTC_CHECK(proc.state() == QProcess::Running);
|
||||||
|
proc.write(QByteArray("yes\n")); // yes to "Do you wish to create a custom hardware profile"
|
||||||
|
|
||||||
|
QByteArray question;
|
||||||
|
while (true) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (proc.state() != QProcess::Running)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
QTC_CHECK(proc.state() == QProcess::NotRunning);
|
||||||
|
|
||||||
|
QString errorOutput = QString::fromLocal8Bit(proc.readAllStandardError());
|
||||||
|
// The exit code is always 0, so we need to check stderr
|
||||||
|
// For now assume that any output at all indicates a error
|
||||||
|
if (!errorOutput.isEmpty()) {
|
||||||
|
info.error = errorOutput;
|
||||||
|
}
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
QVector<AndroidDeviceInfo>
|
||||||
|
AndroidToolManager::androidVirtualDevices(const Utils::FileName &androidTool,
|
||||||
|
const FileName &sdkLocationPath,
|
||||||
|
const Environment &environment)
|
||||||
|
{
|
||||||
|
QVector<AndroidDeviceInfo> devices;
|
||||||
|
QString output;
|
||||||
|
if (!androidToolCommand(androidTool, QStringList({"list", "avd"}), environment, &output))
|
||||||
|
return devices;
|
||||||
|
|
||||||
|
QStringList avds = output.split('\n');
|
||||||
|
if (avds.empty())
|
||||||
|
return devices;
|
||||||
|
|
||||||
|
while (avds.first().startsWith(QLatin1String("* daemon")))
|
||||||
|
avds.removeFirst(); // remove the daemon logs
|
||||||
|
avds.removeFirst(); // remove "List of devices attached" header line
|
||||||
|
|
||||||
|
bool nextLineIsTargetLine = false;
|
||||||
|
|
||||||
|
AndroidDeviceInfo dev;
|
||||||
|
for (int i = 0; i < avds.size(); i++) {
|
||||||
|
QString line = avds.at(i);
|
||||||
|
if (!line.contains(QLatin1String("Name:")))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
int index = line.indexOf(QLatin1Char(':')) + 2;
|
||||||
|
if (index >= line.size())
|
||||||
|
break;
|
||||||
|
dev.avdname = line.mid(index).trimmed();
|
||||||
|
dev.sdk = -1;
|
||||||
|
dev.cpuAbi.clear();
|
||||||
|
++i;
|
||||||
|
for (; i < avds.size(); ++i) {
|
||||||
|
line = avds.at(i);
|
||||||
|
if (line.contains(QLatin1String("---------")))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (line.contains(QLatin1String("Target:")) || nextLineIsTargetLine) {
|
||||||
|
if (line.contains(QLatin1String("Google APIs"))) {
|
||||||
|
nextLineIsTargetLine = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nextLineIsTargetLine = false;
|
||||||
|
|
||||||
|
int lastIndex = line.lastIndexOf(QLatin1Char(' '));
|
||||||
|
if (lastIndex == -1) // skip line
|
||||||
|
break;
|
||||||
|
QString tmp = line.mid(lastIndex).remove(QLatin1Char(')')).trimmed();
|
||||||
|
Utils::FileName platformPath = sdkLocationPath;
|
||||||
|
platformPath.appendPath(QString("/platforms/android-%1").arg(tmp));
|
||||||
|
dev.sdk = AndroidManager::findApiLevel(platformPath);
|
||||||
|
}
|
||||||
|
if (line.contains(QLatin1String("Tag/ABI:"))) {
|
||||||
|
int lastIndex = line.lastIndexOf(QLatin1Char('/')) + 1;
|
||||||
|
if (lastIndex >= line.size())
|
||||||
|
break;
|
||||||
|
dev.cpuAbi = QStringList(line.mid(lastIndex));
|
||||||
|
} else if (line.contains(QLatin1String("ABI:"))) {
|
||||||
|
int lastIndex = line.lastIndexOf(QLatin1Char(' ')) + 1;
|
||||||
|
if (lastIndex >= line.size())
|
||||||
|
break;
|
||||||
|
dev.cpuAbi = QStringList(line.mid(lastIndex).trimmed());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// armeabi-v7a devices can also run armeabi code
|
||||||
|
if (dev.cpuAbi == QStringList("armeabi-v7a"))
|
||||||
|
dev.cpuAbi << QLatin1String("armeabi");
|
||||||
|
dev.state = AndroidDeviceInfo::OkState;
|
||||||
|
dev.type = AndroidDeviceInfo::Emulator;
|
||||||
|
if (dev.cpuAbi.isEmpty() || dev.sdk == -1)
|
||||||
|
continue;
|
||||||
|
devices.push_back(dev);
|
||||||
|
}
|
||||||
|
Utils::sort(devices);
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AndroidToolOutputParser::parseTargetListing(const QString &output,
|
||||||
|
const Utils::FileName &sdkLocation,
|
||||||
|
SdkPlatformList *platformList)
|
||||||
|
{
|
||||||
|
if (!platformList)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SdkPlatform platform;
|
||||||
|
foreach (const QString &l, output.split('\n')) {
|
||||||
|
const QString line = l.trimmed();
|
||||||
|
if (line.startsWith(QLatin1String("id:")) && line.contains(QLatin1String("android-"))) {
|
||||||
|
int index = line.indexOf(QLatin1String("\"android-"));
|
||||||
|
if (index == -1)
|
||||||
|
continue;
|
||||||
|
QString androidTarget = line.mid(index + 1, line.length() - index - 2);
|
||||||
|
const QString tmp = androidTarget.mid(androidTarget.lastIndexOf(QLatin1Char('-')) + 1);
|
||||||
|
Utils::FileName platformPath = sdkLocation;
|
||||||
|
platformPath.appendPath(QString("/platforms/android-%1").arg(tmp));
|
||||||
|
platform.installedLocation = platformPath;
|
||||||
|
platform.apiLevel = AndroidManager::findApiLevel(platformPath);
|
||||||
|
} else if (line.startsWith(QLatin1String("Name:"))) {
|
||||||
|
platform.name = line.mid(6);
|
||||||
|
} else if (line.startsWith(QLatin1String("Tag/ABIs :"))) {
|
||||||
|
platform.abis = cleanAndroidABIs(line.mid(10).trimmed().split(QLatin1String(", ")));
|
||||||
|
} else if (line.startsWith(QLatin1String("ABIs"))) {
|
||||||
|
platform.abis = cleanAndroidABIs(line.mid(6).trimmed().split(QLatin1String(", ")));
|
||||||
|
} else if (line.startsWith(QLatin1String("---")) || line.startsWith(QLatin1String("==="))) {
|
||||||
|
if (platform.apiLevel == -1)
|
||||||
|
continue;
|
||||||
|
*platformList << platform;
|
||||||
|
platform = SdkPlatform();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// The last parsed Platform.
|
||||||
|
if (platform.apiLevel != -1)
|
||||||
|
*platformList << platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Android
|
||||||
72
src/plugins/android/androidtoolmanager.h
Normal file
72
src/plugins/android/androidtoolmanager.h
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** 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 "utils/fileutils.h"
|
||||||
|
#include "androidconfigurations.h"
|
||||||
|
|
||||||
|
#include <QStringList>
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Android {
|
||||||
|
class AndroidConfig;
|
||||||
|
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
class AndroidToolOutputParser;
|
||||||
|
/*!
|
||||||
|
Wraps the \c android tool's usage. The tool itself is deprecated since SDK tools version 25.3.0.
|
||||||
|
*/
|
||||||
|
class AndroidToolManager
|
||||||
|
{
|
||||||
|
Q_DECLARE_TR_FUNCTIONS(AndroidToolManager)
|
||||||
|
|
||||||
|
public:
|
||||||
|
AndroidToolManager(const AndroidConfig &config);
|
||||||
|
~AndroidToolManager();
|
||||||
|
|
||||||
|
SdkPlatformList availableSdkPlatforms() const;
|
||||||
|
void launchAvdManager() const;
|
||||||
|
|
||||||
|
QFuture<AndroidConfig::CreateAvdInfo> createAvd(AndroidConfig::CreateAvdInfo info) const;
|
||||||
|
bool removeAvd(const QString &name) const;
|
||||||
|
QFuture<QVector<AndroidDeviceInfo> > androidVirtualDevicesFuture() const;
|
||||||
|
|
||||||
|
// Helper methods
|
||||||
|
private:
|
||||||
|
Utils::Environment androidToolEnvironment() const;
|
||||||
|
static AndroidConfig::CreateAvdInfo createAvdImpl(AndroidConfig::CreateAvdInfo info,
|
||||||
|
Utils::FileName androidToolPath, Utils::Environment env);
|
||||||
|
static QVector<AndroidDeviceInfo> androidVirtualDevices(const Utils::FileName &androidTool,
|
||||||
|
const Utils::FileName &sdkLlocationPath,
|
||||||
|
const Utils::Environment &environment);
|
||||||
|
private:
|
||||||
|
const AndroidConfig &m_config;
|
||||||
|
std::unique_ptr<AndroidToolOutputParser> m_parser;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Android
|
||||||
Reference in New Issue
Block a user