Change device selection mechanism on Android

Currently, on deploy/debug steps on Android, an AndroidDeviceDialog
is popped up each time a deployement is done to select a device. This
can be avoidable by using Qt Creator DeviceKitAspect to have the list
of devices easily selectable from the project mini-menu.

This is better than the current way because it:
* reduces the time from deployment to running the app
* reduces the number of clicks
* avoids having to select the same device each time or
* if a default device is selected, this avoids having to go to project
settings to reset the default device to be able to deploy to a new
device.
* it looks cleaner and more compatible with Creator.

Task-number: QTCREATORBUG-23991
Change-Id: Ida4ab7245c1a3b0ca26c5ccdc9a21a072edf0725
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
This commit is contained in:
Assam Boudjelthia
2021-08-22 01:51:57 +03:00
parent 15b6eaa47b
commit 96255208a5
19 changed files with 420 additions and 1074 deletions

View File

@@ -13,7 +13,6 @@ add_qtc_plugin(Android
androiddebugsupport.cpp androiddebugsupport.h
androiddeployqtstep.cpp androiddeployqtstep.h
androiddevice.cpp androiddevice.h
androiddevicedialog.cpp androiddevicedialog.h androiddevicedialog.ui
androiddeviceinfo.cpp androiddeviceinfo.h
androiderrormessage.cpp androiderrormessage.h
androidextralibrarylistmodel.cpp androidextralibrarylistmodel.h

View File

@@ -33,7 +33,6 @@ HEADERS += \
androidmanifesteditor.h \
androidmanifesteditorwidget.h \
androidmanifestdocument.h \
androiddevicedialog.h \
androiddeployqtstep.h \
certificatesmodel.h \
androidpotentialkit.h \
@@ -82,7 +81,6 @@ SOURCES += \
androidmanifesteditor.cpp \
androidmanifesteditorwidget.cpp \
androidmanifestdocument.cpp \
androiddevicedialog.cpp \
androiddeployqtstep.cpp \
certificatesmodel.cpp \
androidpotentialkit.cpp \
@@ -109,7 +107,6 @@ FORMS += \
androidsettingswidget.ui \
addnewavddialog.ui \
androidcreatekeystorecertificate.ui \
androiddevicedialog.ui \
androidsdkmanagerwidget.ui
RESOURCES = android.qrc

View File

@@ -35,9 +35,6 @@ Project {
"androiddeployqtstep.h",
"androiddebugsupport.cpp",
"androiddebugsupport.h",
"androiddevicedialog.cpp",
"androiddevicedialog.h",
"androiddevicedialog.ui",
"androiddevice.cpp",
"androiddevice.h",
"androiddeviceinfo.cpp",

View File

@@ -29,7 +29,6 @@
#include "androiddevice.h"
#include "androidmanager.h"
#include "androidqtversion.h"
#include "androiddevicedialog.h"
#include "avddialog.h"
#include <coreplugin/icore.h>
@@ -691,6 +690,26 @@ QString AndroidConfig::getAvdName(const QString &serialnumber)
return QString::fromLatin1(name).trimmed();
}
QStringList AndroidConfig::getRunningAvdsFromDevices(const QVector<AndroidDeviceInfo> &devs)
{
QStringList runningDevs;
for (const AndroidDeviceInfo &dev : devs) {
if (!dev.serialNumber.startsWith("emulator"))
continue;
QStringList args = AndroidDeviceInfo::adbSelector(dev.serialNumber);
args.append({"emu", "avd", "name"});
SdkToolResult result = AndroidManager::runAdbCommand(args);
const QString stdOut = result.stdOut();
if (stdOut.isEmpty())
continue; // Not an avd
const QStringList outputLines = stdOut.split('\n');
if (outputLines.size() > 1)
runningDevs.append(outputLines.first());
}
return runningDevs;
}
AndroidConfig::OpenGl AndroidConfig::getOpenGLEnabled(const QString &emulator) const
{
QDir dir = QDir::home();
@@ -1067,54 +1086,6 @@ void AndroidConfigurations::setConfig(const AndroidConfig &devConfigs)
emit m_instance->updated();
}
AndroidDeviceInfo AndroidConfigurations::showDeviceDialog(Project *project,
int apiLevel, const QStringList &abis)
{
QString serialNumber;
for (const QString &abi : abis) {
serialNumber = defaultDevice(project, abi);
if (!serialNumber.isEmpty())
break;
}
const AndroidDeviceInfo defaultDevice = AndroidDeviceDialog::defaultDeviceInfo(serialNumber);
if (defaultDevice.isValid())
return defaultDevice;
AndroidDeviceDialog dialog(apiLevel, abis, serialNumber, Core::ICore::dialogParent());
AndroidDeviceInfo info = dialog.showAndGetSelectedDevice();
if (dialog.saveDeviceSelection() && info.isValid()) {
const QString newSerialNumber = info.type == AndroidDeviceInfo::Hardware ?
info.serialNumber : info.avdname;
if (!newSerialNumber.isEmpty()) {
const QString preferredAbi = AndroidManager::devicePreferredAbi(info.cpuAbi, abis);
AndroidConfigurations::setDefaultDevice(project, preferredAbi, newSerialNumber);
}
}
return info;
}
void AndroidConfigurations::clearDefaultDevices(Project *project)
{
if (m_instance->m_defaultDeviceForAbi.contains(project))
m_instance->m_defaultDeviceForAbi.remove(project);
}
void AndroidConfigurations::setDefaultDevice(Project *project, const QString &abi, const QString &serialNumber)
{
m_instance->m_defaultDeviceForAbi[project][abi] = serialNumber;
}
QString AndroidConfigurations::defaultDevice(Project *project, const QString &abi)
{
if (!m_instance->m_defaultDeviceForAbi.contains(project))
return QString();
const QMap<QString, QString> &map = m_instance->m_defaultDeviceForAbi.value(project);
if (!map.contains(abi))
return QString();
return map.value(abi);
}
static bool matchToolChain(const ToolChain *atc, const ToolChain *btc)
{
if (atc == btc)
@@ -1306,15 +1277,6 @@ void AndroidConfigurations::updateAutomaticKitList()
qtVersionsForArch[qtAbis.first()].append(qtVersion);
}
DeviceManager *dm = DeviceManager::instance();
IDevice::ConstPtr device = dm->find(Constants::ANDROID_DEVICE_ID);
if (device.isNull()) {
// no device, means no sdk path
for (Kit *k : existingKits)
KitManager::deregisterKit(k);
return;
}
// register new kits
const QList<ToolChain *> toolchains = ToolChainManager::toolChains([](const ToolChain *tc) {
return tc->isAutoDetected()
@@ -1350,20 +1312,20 @@ void AndroidConfigurations::updateAutomaticKitList()
ToolChainKitAspect::cToolChain(b));
});
const auto initializeKit = [allLanguages, device, tc, qt](Kit *k) {
const auto initializeKit = [allLanguages, tc, qt](Kit *k) {
k->setAutoDetected(true);
k->setAutoDetectionSource("AndroidConfiguration");
DeviceTypeKitAspect::setDeviceTypeId(k, Constants::ANDROID_DEVICE_TYPE);
for (ToolChain *tc : allLanguages)
ToolChainKitAspect::setToolChain(k, tc);
QtKitAspect::setQtVersion(k, qt);
DeviceKitAspect::setDevice(k, device);
QStringList abis = static_cast<const AndroidQtVersion *>(qt)->androidAbis();
Debugger::DebuggerKitAspect::setDebugger(k, findOrRegisterDebugger(tc, abis));
k->setSticky(ToolChainKitAspect::id(), true);
k->setSticky(QtKitAspect::id(), true);
k->setSticky(DeviceKitAspect::id(), true);
k->setMutable(DeviceKitAspect::id(), true);
k->setSticky(DeviceTypeKitAspect::id(), true);
QString versionStr = QLatin1String("Qt %{Qt:Version}");
@@ -1433,14 +1395,10 @@ AndroidConfigurations::AndroidConfigurations()
: m_sdkManager(new AndroidSdkManager(m_config))
{
load();
connect(SessionManager::instance(), &SessionManager::projectRemoved,
this, &AndroidConfigurations::clearDefaultDevices);
connect(DeviceManager::instance(), &DeviceManager::devicesLoaded,
this, &AndroidConfigurations::updateAndroidDevice);
m_force32bit = is32BitUserSpace();
m_instance = this;
}
@@ -1545,11 +1503,13 @@ void AndroidConfigurations::load()
void AndroidConfigurations::updateAndroidDevice()
{
DeviceManager * const devMgr = DeviceManager::instance();
if (m_instance->m_config.adbToolPath().exists())
devMgr->addDevice(AndroidDevice::create());
else if (devMgr->find(Constants::ANDROID_DEVICE_ID))
devMgr->removeDevice(Constants::ANDROID_DEVICE_ID);
// Remove any dummy Android device, because it won't be usable.
DeviceManager *const devMgr = DeviceManager::instance();
IDevice::ConstPtr dev = devMgr->find(Constants::ANDROID_DEVICE_ID);
if (dev)
devMgr->removeDevice(dev->id());
AndroidDeviceManager::instance()->setupDevicesWatcher();
}
AndroidConfigurations *AndroidConfigurations::m_instance = nullptr;

View File

@@ -171,6 +171,8 @@ public:
static Utils::FilePath getJdkPath();
static QStringList getRunningAvdsFromDevices(const QVector<AndroidDeviceInfo> &devs);
private:
static QString getDeviceProperty(const Utils::FilePath &adbToolPath,
const QString &device, const QString &property);
@@ -213,10 +215,6 @@ public:
static void setConfig(const AndroidConfig &config);
static AndroidConfigurations *instance();
static AndroidDeviceInfo showDeviceDialog(ProjectExplorer::Project *project, int apiLevel, const QStringList &abis);
static void setDefaultDevice(ProjectExplorer::Project *project, const QString &abi, const QString &serialNumber); // serial number or avd name
static QString defaultDevice(ProjectExplorer::Project *project, const QString &abi); // serial number or avd name
static void clearDefaultDevices(ProjectExplorer::Project *project);
static void registerNewToolChains();
static void registerCustomToolChainsAndDebuggers();
static void removeUnusedDebuggers();
@@ -240,8 +238,6 @@ private:
static AndroidConfigurations *m_instance;
AndroidConfig m_config;
std::unique_ptr<Internal::AndroidSdkManager> m_sdkManager;
QMap<ProjectExplorer::Project *, QMap<QString, QString> > m_defaultDeviceForAbi;
bool m_force32bit;
};

View File

@@ -26,6 +26,7 @@
#pragma once
#include <QtGlobal>
#include <utils/id.h>
namespace Android {
namespace Internal {
@@ -93,5 +94,15 @@ const char NdkLocation[] = "NdkLocation"; // FileName
const char SdkLocation[] = "SdkLocation"; // FileName
const char AndroidABIs[] = "AndroidABIs"; // QString
// Android Device
const Utils::Id AndroidSerialNumber = "AndroidSerialNumber";
const Utils::Id AndroidAvdName = "AndroidAvdName";
const Utils::Id AndroidCpuAbi = "AndroidCpuAbi";
const Utils::Id AndroidAvdTarget = "AndroidAvdTarget";
const Utils::Id AndroidAvdDevice = "AndroidAvdDevice";
const Utils::Id AndroidAvdSkin = "AndroidAvdSkin";
const Utils::Id AndroidAvdSdcard = "AndroidAvdSdcard";
const Utils::Id AndroidSdk = "AndroidSdk";
} // namespace Constants;
} // namespace Android

View File

@@ -33,6 +33,7 @@
#include "androidglobal.h"
#include "androidavdmanager.h"
#include "androidqtversion.h"
#include "androiddevice.h"
#include <coreplugin/fileutils.h>
#include <coreplugin/icore.h>
@@ -147,14 +148,6 @@ bool AndroidDeployQtStep::init()
if (androidDeployQtStep != this)
info = androidDeployQtStep->m_deviceInfo;
if (!info.isValid()) {
info = AndroidConfigurations::showDeviceDialog(project(), minTargetApi, m_androidABIs);
m_deviceInfo = info; // Keep around for later steps
}
if (!info.isValid()) // aborted
return false;
const BuildSystem *bs = buildSystem();
auto selectedAbis = bs->property(Constants::ANDROID_ABIS).toStringList();
@@ -165,6 +158,40 @@ bool AndroidDeployQtStep::init()
if (selectedAbis.isEmpty())
selectedAbis.append(bs->extraData(buildKey, Constants::AndroidArch).toString());
if (!info.isValid()) {
const IDevice *dev = DeviceKitAspect::device(kit()).data();
info = AndroidDevice::androidDeviceInfoFromIDevice(dev);
m_deviceInfo = info; // Keep around for later steps
if (!info.isValid()) {
const QString error = tr("The deployment device \"%1\" is invalid.")
.arg(dev->displayName());
emit addOutput(error, OutputFormat::Stderr);
TaskHub::addTask(DeploymentTask(Task::Error, error));
return false;
}
const AndroidDevice *androidDev = static_cast<const AndroidDevice *>(dev);
if (androidDev && !androidDev->canSupportAbis(selectedAbis)) {
const QString error = tr("The deployment device \"%1\" doesn't support the "
"architectures used by the kit.\n"
"The kit supports \"%2\", but the device uses \"%3\".")
.arg(dev->displayName()).arg(selectedAbis.join(", "))
.arg(androidDev->supportedAbis().join(", "));
emit addOutput(error, OutputFormat::Stderr);
TaskHub::addTask(DeploymentTask(Task::Error, error));
return false;
}
if (androidDev && !androidDev->canHandleDeployments()) {
const QString error = tr("The deployment device \"%1\" is disconnected.")
.arg(dev->displayName());
emit addOutput(error, OutputFormat::Stderr);
TaskHub::addTask(DeploymentTask(Task::Error, error));
return false;
}
}
const QtSupport::BaseQtVersion * const qt = QtSupport::QtKitAspect::qtVersion(kit());
if (qt && qt->supportsMultipleQtAbis() && !selectedAbis.contains(info.cpuAbi.first())) {
TaskHub::addTask(DeploymentTask(
@@ -500,14 +527,6 @@ void AndroidDeployQtStep::runCommand(const CommandLine &command)
QWidget *AndroidDeployQtStep::createConfigWidget()
{
auto widget = new QWidget;
auto resetDefaultDevices = new QPushButton(widget);
resetDefaultDevices->setText(tr("Reset Default Deployment Devices"));
connect(resetDefaultDevices, &QAbstractButton::clicked, this, [this] {
AndroidConfigurations::clearDefaultDevices(project());
});
auto installCustomApkButton = new QPushButton(widget);
installCustomApkButton->setText(tr("Install an APK File"));
@@ -523,7 +542,6 @@ QWidget *AndroidDeployQtStep::createConfigWidget()
Layouting::Form builder;
builder.addRow(m_uninstallPreviousPackage);
builder.addRow(resetDefaultDevices);
builder.addRow(installCustomApkButton);
builder.attachTo(widget);

View File

@@ -1,5 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2016 BogDan Vatra <bog_dan_ro@yahoo.com>
** Contact: https://www.qt.io/licensing/
**
@@ -24,19 +25,29 @@
****************************************************************************/
#include "androiddevice.h"
#include "androidavdmanager.h"
#include "androidconfigurations.h"
#include "androidconstants.h"
#include "androidsignaloperation.h"
#include "androidconfigurations.h"
#include "androidmanager.h"
#include <projectexplorer/devicesupport/devicemanager.h>
#include <projectexplorer/runconfiguration.h>
#include <utils/url.h>
#include <utils/runextensions.h>
#include <QLoggingCategory>
using namespace ProjectExplorer;
namespace {
static Q_LOGGING_CATEGORY(androidDeviceLog, "qtc.android.androiddevice", QtWarningMsg)
}
// interval for updating the list of connected Android devices and emulators
constexpr int deviceUpdaterMsInterval = 30000;
namespace Android {
namespace Internal {
@@ -48,8 +59,151 @@ AndroidDevice::AndroidDevice()
setDisplayType(tr("Android"));
setMachineType(IDevice::Hardware);
setOsType(Utils::OsTypeOtherUnix);
setDeviceState(DeviceConnected);
setDeviceState(DeviceReadyToUse);
addDeviceAction({tr("Refresh"), [](const IDevice::Ptr &device, QWidget *parent) {
AndroidDeviceManager::instance()->updateDevicesListOnce();
}});
}
IDevice::Ptr AndroidDevice::create()
{
return IDevice::Ptr(new AndroidDevice);
}
AndroidDeviceInfo AndroidDevice::androidDeviceInfoFromIDevice(const IDevice *dev)
{
AndroidDeviceInfo info;
AndroidDeviceInfo::State state;
if (dev->deviceState() == IDevice::DeviceReadyToUse)
state = AndroidDeviceInfo::OkState;
else if (dev->deviceState() == IDevice::DeviceDisconnected)
state = AndroidDeviceInfo::OfflineState;
else if (dev->deviceState() == IDevice::DeviceConnected)
state = AndroidDeviceInfo::UnAuthorizedState;
info.state = state;
info.avdname = dev->extraData(Constants::AndroidAvdName).toString();
info.serialNumber = dev->extraData(Constants::AndroidSerialNumber).toString();
info.cpuAbi = dev->extraData(Constants::AndroidCpuAbi).toStringList();
info.avdTarget = dev->extraData(Constants::AndroidAvdTarget).toString();
info.avdDevice = dev->extraData(Constants::AndroidAvdDevice).toString();
info.avdSkin = dev->extraData(Constants::AndroidAvdSkin).toString();
info.avdSdcardSize = dev->extraData(Constants::AndroidAvdSdcard).toString();
info.sdk = dev->extraData(Constants::AndroidSdk).toInt();
info.type = (dev->machineType() == ProjectExplorer::IDevice::Hardware
? AndroidDeviceInfo::Hardware : AndroidDeviceInfo::Emulator);
return info;
}
void AndroidDevice::setAndroidDeviceInfoExtras(IDevice *dev, const AndroidDeviceInfo &info)
{
dev->setMachineType(info.type == AndroidDeviceInfo::Hardware
? ProjectExplorer::IDevice::Hardware
: ProjectExplorer::IDevice::Emulator);
dev->setDeviceState(deviceStateFromInfo(info.state));
dev->setExtraData(Constants::AndroidAvdName, info.avdname);
dev->setExtraData(Constants::AndroidSerialNumber, info.serialNumber);
dev->setExtraData(Constants::AndroidCpuAbi, info.cpuAbi);
dev->setExtraData(Constants::AndroidAvdTarget, info.avdTarget);
dev->setExtraData(Constants::AndroidAvdDevice, info.avdDevice);
dev->setExtraData(Constants::AndroidAvdSkin, info.avdSkin);
dev->setExtraData(Constants::AndroidAvdSdcard, info.avdSdcardSize);
dev->setExtraData(Constants::AndroidSdk, info.sdk);
}
QString AndroidDevice::displayNameFromInfo(const AndroidDeviceInfo &info)
{
return info.type == AndroidDeviceInfo::Hardware
? AndroidConfigurations::currentConfig().getProductModel(info.serialNumber)
: info.avdname;
}
Utils::Id AndroidDevice::idFromDeviceInfo(const AndroidDeviceInfo &info)
{
const QString id = (info.type == AndroidDeviceInfo::Hardware ? info.serialNumber
: info.avdname);
return Utils::Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + id);
}
Utils::Id AndroidDevice::idFromAvdInfo(const CreateAvdInfo &info)
{
return Utils::Id(Constants::ANDROID_DEVICE_ID).withSuffix(':' + info.name);
}
IDevice::DeviceState AndroidDevice::deviceStateFromInfo(AndroidDeviceInfo::State state)
{
if (state == AndroidDeviceInfo::OkState)
return IDevice::DeviceReadyToUse;
if (state == AndroidDeviceInfo::OfflineState)
return IDevice::DeviceDisconnected;
return IDevice::DeviceConnected;
}
QStringList AndroidDevice::supportedAbis() const
{
return extraData(Constants::AndroidCpuAbi).toStringList();
}
bool AndroidDevice::canSupportAbis(const QStringList &abis) const
{
// If the list is empty, no valid decision can be made, this means something is wrong
// somewhere, but let's not stop deployment.
QTC_ASSERT(!abis.isEmpty(), return true);
const QStringList ourAbis = supportedAbis();
QTC_ASSERT(!ourAbis.isEmpty(), return false);
for (const QString &abi : abis)
if (ourAbis.contains(abi))
return true; // it's enough if only one abi match is found
// If no exact match is found, let's take ABI backward compatibility into account
// https://developer.android.com/ndk/guides/abis#android-platform-abi-support
// arm64 usually can run {arm, armv7}, x86 can support {arm, armv7}, and 64-bit devices
// can support their 32-bit variants.
using namespace ProjectExplorer::Constants;
const bool isTheirsArm = abis.contains(ANDROID_ABI_ARMEABI_V7A)
|| abis.contains(ANDROID_ABI_ARMEABI_V7A);
// The primary ABI at the first index
const bool oursSupportsArm = ourAbis.first() == ANDROID_ABI_ARM64_V8A
|| ourAbis.first() == ANDROID_ABI_X86;
// arm64 and x86 can run armv7 and arm
if (isTheirsArm && oursSupportsArm)
return true;
// x64 can run x86
if (ourAbis.first() == ANDROID_ABI_X86_64 && abis.contains(ANDROID_ABI_X86))
return true;
return false;
}
bool AndroidDevice::canHandleDeployments() const
{
// If hardware and disconned, it wouldn't be possilbe to start it, unlike an emulator
if (machineType() == Hardware && deviceState() == DeviceDisconnected)
return false;
return true;
}
bool AndroidDevice::isValid() const
{
return !serialNumber().isEmpty() || !avdName().isEmpty();
}
QString AndroidDevice::serialNumber() const
{
return extraData(Constants::AndroidSerialNumber).toString();
}
QString AndroidDevice::avdName() const
{
return extraData(Constants::AndroidAvdName).toString();
}
int AndroidDevice::sdkLevel() const
{
return extraData(Constants::AndroidSdk).toInt();
}
IDevice::DeviceInfo AndroidDevice::deviceInformation() const
@@ -80,9 +234,128 @@ QUrl AndroidDevice::toolControlChannel(const ControlChannelHint &) const
return url;
}
void AndroidDeviceManager::updateDevicesList()
{
connect(&m_devicesUpdaterTimer, &QTimer::timeout, this, [this]() {
updateDevicesListOnce();
});
updateDevicesListOnce();
m_devicesUpdaterTimer.start(deviceUpdaterMsInterval);
}
void AndroidDeviceManager::updateDevicesListOnce()
{
if (!m_avdsFutureWatcher.isRunning() && m_androidConfig.adbToolPath().exists()) {
m_avdsFutureWatcher.setFuture((new AndroidAvdManager)->avdList());
m_devicesFutureWatcher.setFuture(Utils::runAsync([this]() {
return m_androidConfig.connectedDevices();
}));
}
}
void AndroidDeviceManager::setupDevicesWatcher()
{
// The call to avdmanager is always slower than the call to adb devices,
// so connecting the slot to the slower call should be enough.
connect(&m_avdsFutureWatcher, &QFutureWatcherBase::finished,
this, &AndroidDeviceManager::devicesListUpdated);
updateDevicesList();
}
void AndroidDeviceManager::devicesListUpdated()
{
QVector<AndroidDeviceInfo> connectedDevicesInfos;
connectedDevicesInfos = m_devicesFutureWatcher.result();
// For checking the state of avds, since running avds are assigned a serial number of
// the form emulator-xxxx, thus we have to manually check for the names.
const QStringList runningAvds = m_androidConfig.getRunningAvdsFromDevices(connectedDevicesInfos);
AndroidDeviceInfoList devices = m_avdsFutureWatcher.result();
const QSet<QString> startedAvds = Utils::transform<QSet>(connectedDevicesInfos,
&AndroidDeviceInfo::avdname);
for (const AndroidDeviceInfo &dev : devices)
if (!startedAvds.contains(dev.avdname))
connectedDevicesInfos << dev;
DeviceManager *const devMgr = DeviceManager::instance();
QVector<IDevice::ConstPtr> existingDevs;
QVector<IDevice::ConstPtr> connectedDevs;
for (int i = 0; i < devMgr->deviceCount(); ++i) {
const IDevice::ConstPtr dev = devMgr->deviceAt(i);
if (dev->id().toString().startsWith(Constants::ANDROID_DEVICE_ID)) {
existingDevs.append(dev);
}
}
for (auto item : connectedDevicesInfos) {
const Utils::Id deviceId = AndroidDevice::idFromDeviceInfo(item);
const QString displayName = AndroidDevice::displayNameFromInfo(item);
IDevice::ConstPtr dev = devMgr->find(deviceId);
if (!dev.isNull()) {
if (dev->displayName() == displayName) {
IDevice::DeviceState newState;
// If an AVD is not already running set its state to Connected instead of
// ReadyToUse.
if (dev->machineType() == IDevice::Emulator && !runningAvds.contains(displayName))
newState = IDevice::DeviceConnected;
else
newState = AndroidDevice::deviceStateFromInfo(item.state);
if (dev->deviceState() != newState) {
qCDebug(androidDeviceLog, "Device id \"%s\" changed its state.",
dev->id().toString().toUtf8().data());
devMgr->setDeviceState(dev->id(), newState);
}
connectedDevs.append(dev);
continue;
} else {
// DeviceManager doens't seem to hav a way to directly update the name, if the name
// of the device has changed, remove it and register it again with the new name.
devMgr->removeDevice(dev->id());
}
}
AndroidDevice *newDev = new AndroidDevice();
newDev->setupId(IDevice::AutoDetected, deviceId);
newDev->setDisplayName(displayName);
AndroidDevice::setAndroidDeviceInfoExtras(newDev, item);
qCDebug(androidDeviceLog, "Registering new Android device id \"%s\".",
newDev->id().toString().toUtf8().data());
const IDevice::ConstPtr constNewDev = IDevice::ConstPtr(newDev);
devMgr->addDevice(constNewDev);
connectedDevs.append(constNewDev);
}
// Set devices no longer connected to disconnected state.
for (const IDevice::ConstPtr dev : existingDevs) {
if (dev->id() != Constants::ANDROID_DEVICE_ID && !connectedDevs.contains(dev)
&& dev->deviceState() != IDevice::DeviceDisconnected) {
qCDebug(androidDeviceLog, "Device id \"%s\" is no longer connected.",
dev->id().toString().toUtf8().data());
devMgr->setDeviceState(dev->id(), IDevice::DeviceDisconnected);
}
}
}
AndroidDeviceManager *AndroidDeviceManager::instance()
{
static AndroidDeviceManager obj;
return &obj;
}
AndroidDeviceManager::AndroidDeviceManager(QObject *parent)
: m_androidConfig(AndroidConfigurations::currentConfig())
{
connect(qApp, &QCoreApplication::aboutToQuit, this, [this]() {
m_devicesUpdaterTimer.stop();
m_avdsFutureWatcher.waitForFinished();
m_devicesFutureWatcher.waitForFinished();
});
}
// Factory
AndroidDeviceFactory::AndroidDeviceFactory()
: ProjectExplorer::IDeviceFactory(Constants::ANDROID_DEVICE_TYPE)
{

View File

@@ -1,5 +1,6 @@
/****************************************************************************
**
** Copyright (C) 2021 The Qt Company Ltd.
** Copyright (C) 2016 BogDan Vatra <bog_dan_ro@yahoo.com>
** Contact: https://www.qt.io/licensing/
**
@@ -25,9 +26,15 @@
#pragma once
#include "androidconfigurations.h"
#include "androiddeviceinfo.h"
#include <projectexplorer/devicesupport/idevice.h>
#include <projectexplorer/devicesupport/idevicefactory.h>
#include <QFutureWatcher>
#include <QTimer>
namespace Android {
namespace Internal {
@@ -36,17 +43,32 @@ class AndroidDevice final : public ProjectExplorer::IDevice
Q_DECLARE_TR_FUNCTIONS(Android::Internal::AndroidDevice)
public:
static IDevice::Ptr create() { return IDevice::Ptr(new AndroidDevice); }
private:
AndroidDevice();
ProjectExplorer::IDevice::DeviceInfo deviceInformation() const override;
static IDevice::Ptr create();
static AndroidDeviceInfo androidDeviceInfoFromIDevice(const IDevice *dev);
static void setAndroidDeviceInfoExtras(IDevice *dev, const AndroidDeviceInfo &info);
static QString displayNameFromInfo(const AndroidDeviceInfo &info);
static Utils::Id idFromDeviceInfo(const AndroidDeviceInfo &info);
static Utils::Id idFromAvdInfo(const CreateAvdInfo &info);
static IDevice::DeviceState deviceStateFromInfo(AndroidDeviceInfo::State state);
QStringList supportedAbis() const;
bool canSupportAbis(const QStringList &abis) const;
bool canHandleDeployments() const;
bool isValid() const;
QString serialNumber() const;
QString avdName() const;
int sdkLevel() const;
private:
ProjectExplorer::IDevice::DeviceInfo deviceInformation() const override;
ProjectExplorer::IDeviceWidget *createWidget() override;
bool canAutoDetectPorts() const override;
ProjectExplorer::DeviceProcessSignalOperation::Ptr signalOperation() const override;
QUrl toolControlChannel(const ControlChannelHint &) const override;
};
@@ -56,5 +78,23 @@ public:
AndroidDeviceFactory();
};
class AndroidDeviceManager : public QObject
{
public:
static AndroidDeviceManager *instance();
void setupDevicesWatcher();
void updateDevicesList();
void updateDevicesListOnce();
private:
AndroidDeviceManager(QObject *parent = nullptr);
void devicesListUpdated();
QFutureWatcher<AndroidDeviceInfoList> m_avdsFutureWatcher;
QFutureWatcher<QVector<AndroidDeviceInfo>> m_devicesFutureWatcher;
QTimer m_devicesUpdaterTimer;
AndroidConfig m_androidConfig;
};
} // namespace Internal
} // namespace Android

View File

@@ -1,636 +0,0 @@
/****************************************************************************
**
** 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 "androiddevicedialog.h"
#include "androidmanager.h"
#include "androidavdmanager.h"
#include "avddialog.h"
#include "ui_androiddevicedialog.h"
#include <utils/environment.h>
#include <utils/progressindicator.h>
#include <utils/algorithm.h>
#include <QMessageBox>
#include <QPainter>
#include <QStyledItemDelegate>
#include <QToolTip>
using namespace Android;
using namespace Android::Internal;
namespace Android {
namespace Internal {
QVector<AndroidDeviceInfo> AndroidDeviceDialog::m_connectedDevices = {};
// yeah, writing tree models is fun!
class AndroidDeviceModelNode
{
public:
AndroidDeviceModelNode(AndroidDeviceModelNode *parent, const AndroidDeviceInfo &info, const QString &incompatibleReason = QString())
: m_parent(parent), m_info(info), m_incompatibleReason(incompatibleReason)
{
if (m_parent)
m_parent->m_children.append(this);
}
AndroidDeviceModelNode(AndroidDeviceModelNode *parent, const QString &displayName)
: m_parent(parent), m_displayName(displayName)
{
if (m_parent)
m_parent->m_children.append(this);
}
~AndroidDeviceModelNode()
{
if (m_parent)
m_parent->m_children.removeOne(this);
QList<AndroidDeviceModelNode *> children = m_children;
qDeleteAll(children);
}
AndroidDeviceModelNode *parent() const
{
return m_parent;
}
QList<AndroidDeviceModelNode *> children() const
{
return m_children;
}
AndroidDeviceInfo deviceInfo() const
{
return m_info;
}
QString displayName() const
{
return m_displayName;
}
QString incompatibleReason() const
{
return m_incompatibleReason;
}
private:
AndroidDeviceModelNode *m_parent;
AndroidDeviceInfo m_info;
QString m_incompatibleReason;
QString m_displayName;
QList<AndroidDeviceModelNode *> m_children;
};
class AndroidDeviceModelDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
AndroidDeviceModelDelegate(QObject * parent = nullptr)
: QStyledItemDelegate(parent)
{
}
~AndroidDeviceModelDelegate() override = default;
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
painter->save();
auto node = static_cast<AndroidDeviceModelNode *>(index.internalPointer());
AndroidDeviceInfo device = node->deviceInfo();
painter->setPen(Qt::NoPen);
// Paint Background
QPalette palette = opt.palette; // we always draw enabled
palette.setCurrentColorGroup(QPalette::Active);
bool selected = opt.state & QStyle::State_Selected;
QColor backgroundColor = selected ? palette.highlight().color()
: palette.window().color();
painter->setBrush(backgroundColor);
painter->drawRect(0, opt.rect.top(), opt.rect.width() + opt.rect.left(), opt.rect.height());
QColor textColor;
// Set Text Color
if (opt.state & QStyle::State_Selected)
textColor = palette.highlightedText().color();
else
textColor = palette.text().color();
painter->setPen(textColor);
if (!node->displayName().isEmpty()) { // Title
// We have a top level node
QFont font = opt.font;
font.setPointSizeF(font.pointSizeF() * 1.2);
font.setBold(true);
QFontMetrics fm(font);
painter->setFont(font);
int top = (opt.rect.bottom() + opt.rect.top() - fm.height()) / 2 + fm.ascent();
painter->drawText(6, top, node->displayName());
} else {
QIcon icon(device.type == AndroidDeviceInfo::Hardware ? QLatin1String(":/projectexplorer/images/MaemoDevice.png")
: QLatin1String(":/projectexplorer/images/Simulator.png"));
int size = opt.rect.bottom() - opt.rect.top() - 12;
QPixmap pixmap = icon.pixmap(size, size);
painter->drawPixmap(6 + (size - pixmap.width()) / 2, opt.rect.top() + 6 + (size - pixmap.width()) / 2, pixmap);
QFontMetrics fm(opt.font);
// TopLeft
QString topLeft;
if (device.type == AndroidDeviceInfo::Hardware)
topLeft = AndroidConfigurations::currentConfig().getProductModel(device.serialNumber);
else
topLeft = device.avdname;
painter->drawText(size + 12, 2 + opt.rect.top() + fm.ascent(), topLeft);
// topRight
auto drawTopRight = [&](const QString text, const QFontMetrics &fm) {
painter->drawText(opt.rect.right() - fm.horizontalAdvance(text) - 6 , 2 + opt.rect.top() + fm.ascent(), text);
};
if (device.type == AndroidDeviceInfo::Hardware) {
drawTopRight(device.serialNumber, fm);
} else {
AndroidConfig::OpenGl openGl = AndroidConfigurations::currentConfig().getOpenGLEnabled(device.avdname);
if (openGl == AndroidConfig::OpenGl::Enabled) {
drawTopRight(tr("OpenGL enabled"), fm);
} else if (openGl == AndroidConfig::OpenGl::Disabled) {
QFont font = painter->font();
font.setBold(true);
painter->setFont(font);
QFontMetrics fmBold(font);
drawTopRight(tr("OpenGL disabled"), fmBold);
font.setBold(false);
painter->setFont(font);
}
}
// Directory
QColor mix;
mix.setRgbF(0.7 * textColor.redF() + 0.3 * backgroundColor.redF(),
0.7 * textColor.greenF() + 0.3 * backgroundColor.greenF(),
0.7 * textColor.blueF() + 0.3 * backgroundColor.blueF());
painter->setPen(mix);
QString lineText;
if (node->incompatibleReason().isEmpty()) {
lineText = AndroidManager::androidNameForApiLevel(device.sdk) + QLatin1String(" ");
lineText += AndroidDeviceDialog::tr("ABI:") + device.cpuAbi.join(QLatin1Char(' '));
} else {
lineText = node->incompatibleReason();
QFont f = painter->font();
f.setBold(true);
painter->setFont(f);
}
painter->drawText(size + 12, opt.rect.top() + fm.ascent() + fm.height() + 6, lineText);
}
// Separator lines
painter->setPen(QColor::fromRgb(150,150,150));
painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
painter->restore();
}
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
{
QStyleOptionViewItem opt = option;
initStyleOption(&opt, index);
QFontMetrics fm(option.font);
QSize s;
s.setWidth(option.rect.width());
s.setHeight(fm.height() * 2 + 10);
return s;
}
};
class AndroidDeviceModel : public QAbstractItemModel
{
Q_OBJECT
public:
AndroidDeviceModel(int apiLevel, const QStringList &abis);
QModelIndex index(int row, int column,
const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
Qt::ItemFlags flags(const QModelIndex &index) const override;
AndroidDeviceInfo device(QModelIndex index);
void setDevices(const QVector<AndroidDeviceInfo> &devices);
QModelIndex indexFor(AndroidDeviceInfo::AndroidDeviceType type, const QString &serial);
private:
int m_apiLevel;
QStringList m_abis;
AndroidDeviceModelNode *m_root;
};
}
}
/////////////////
// AndroidDeviceModel
/////////////////
AndroidDeviceModel::AndroidDeviceModel(int apiLevel, const QStringList &abis)
: m_apiLevel(apiLevel), m_abis(abis), m_root(nullptr)
{
}
QModelIndex AndroidDeviceModel::index(int row, int column, const QModelIndex &parent) const
{
if (column != 0)
return QModelIndex();
if (!m_root)
return QModelIndex();
if (!parent.isValid()) {
if (row < 0 || row >= m_root->children().count())
return QModelIndex();
return createIndex(row, column, m_root->children().at(row));
}
auto node = static_cast<AndroidDeviceModelNode *>(parent.internalPointer());
if (row < node->children().count())
return createIndex(row, column, node->children().at(row));
return QModelIndex();
}
QModelIndex AndroidDeviceModel::parent(const QModelIndex &child) const
{
if (!child.isValid())
return QModelIndex();
if (!m_root)
return QModelIndex();
auto node = static_cast<AndroidDeviceModelNode *>(child.internalPointer());
if (node == m_root)
return QModelIndex();
AndroidDeviceModelNode *parent = node->parent();
if (parent == m_root)
return QModelIndex();
AndroidDeviceModelNode *grandParent = parent->parent();
return createIndex(grandParent->children().indexOf(parent), 0, parent);
}
int AndroidDeviceModel::rowCount(const QModelIndex &parent) const
{
if (!m_root)
return 0;
if (!parent.isValid())
return m_root->children().count();
auto node = static_cast<AndroidDeviceModelNode *>(parent.internalPointer());
return node->children().count();
}
int AndroidDeviceModel::columnCount(const QModelIndex &parent) const
{
Q_UNUSED(parent)
return 1;
}
QVariant AndroidDeviceModel::data(const QModelIndex &index, int role) const
{
if (role != Qt::DisplayRole)
return QVariant();
auto node = static_cast<AndroidDeviceModelNode *>(index.internalPointer());
if (!node)
return QVariant();
return node->deviceInfo().serialNumber;
}
Qt::ItemFlags AndroidDeviceModel::flags(const QModelIndex &index) const
{
auto node = static_cast<AndroidDeviceModelNode *>(index.internalPointer());
if (node)
if (node->displayName().isEmpty() && node->incompatibleReason().isEmpty())
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;
return Qt::NoItemFlags;
}
AndroidDeviceInfo AndroidDeviceModel::device(QModelIndex index)
{
auto node = static_cast<AndroidDeviceModelNode *>(index.internalPointer());
if (!node)
return AndroidDeviceInfo();
return node->deviceInfo();
}
void AndroidDeviceModel::setDevices(const QVector<AndroidDeviceInfo> &devices)
{
beginResetModel();
delete m_root;
m_root = new AndroidDeviceModelNode(nullptr, QString());
AndroidDeviceModelNode *compatibleDevices = new AndroidDeviceModelNode(m_root, AndroidDeviceDialog::tr("Compatible devices"));
AndroidDeviceModelNode *incompatibleDevices = nullptr; // created on demand
foreach (const AndroidDeviceInfo &device, devices) {
QString error;
if (device.state == AndroidDeviceInfo::UnAuthorizedState) {
error = AndroidDeviceDialog::tr("Unauthorized. Please check the confirmation dialog on your device %1.")
.arg(device.serialNumber);
}else if (device.state == AndroidDeviceInfo::OfflineState) {
error = AndroidDeviceDialog::tr("Offline. Please check the state of your device %1.")
.arg(device.serialNumber);
} else if (!AndroidManager::matchedAbis(device.cpuAbi, m_abis)) {
error = AndroidDeviceDialog::tr("ABI is incompatible, device supports ABIs: %1.")
.arg(device.cpuAbi.join(QLatin1Char(' ')));
} else if (device.sdk < m_apiLevel) {
error = AndroidDeviceDialog::tr("API Level of device is: %1.")
.arg(device.sdk);
} else {
new AndroidDeviceModelNode(compatibleDevices, device);
continue;
}
if (!incompatibleDevices)
incompatibleDevices = new AndroidDeviceModelNode(m_root, AndroidDeviceDialog::tr("Incompatible devices"));
new AndroidDeviceModelNode(incompatibleDevices, device, error);
}
endResetModel();
}
QModelIndex AndroidDeviceModel::indexFor(AndroidDeviceInfo::AndroidDeviceType type, const QString &serial)
{
foreach (AndroidDeviceModelNode *topLevelNode, m_root->children()) {
QList<AndroidDeviceModelNode *> deviceNodes = topLevelNode->children();
for (int i = 0; i < deviceNodes.size(); ++i) {
const AndroidDeviceInfo &info = deviceNodes.at(i)->deviceInfo();
if (info.type != type)
continue;
if ((type == AndroidDeviceInfo::Hardware && serial == info.serialNumber)
|| (type == AndroidDeviceInfo::Emulator && serial == info.avdname))
return createIndex(i, 0, deviceNodes.at(i));
}
}
return QModelIndex();
}
/////////////////
// AndroidDeviceDialog
/////////////////
static inline QString msgConnect()
{
return AndroidDeviceDialog::tr("<p>Connect an Android device via USB and activate developer mode on it. "
"Some devices require the installation of a USB driver.</p>");
}
static inline QString msgAdbListDevices()
{
return AndroidDeviceDialog::tr("<p>The adb tool in the Android SDK lists all connected devices if run via &quot;adb devices&quot;.</p>");
}
AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QStringList &abis,
const QString &serialNumber, QWidget *parent) :
QDialog(parent),
m_model(new AndroidDeviceModel(apiLevel, abis)),
m_ui(new Ui::AndroidDeviceDialog),
m_apiLevel(apiLevel),
m_abis(abis),
m_defaultDevice(serialNumber),
m_avdManager(new AndroidAvdManager)
{
m_ui->setupUi(this);
m_ui->deviceView->setModel(m_model);
m_ui->deviceView->setItemDelegate(new AndroidDeviceModelDelegate(m_ui->deviceView));
m_ui->deviceView->setHeaderHidden(true);
m_ui->deviceView->setRootIsDecorated(false);
m_ui->deviceView->setUniformRowHeights(true);
m_ui->deviceView->setExpandsOnDoubleClick(false);
m_ui->defaultDeviceCheckBox->setText(tr("Always use this device for this project"));
m_ui->noDeviceFoundLabel->setText(QLatin1String("<p align=\"center\"><span style=\" font-size:16pt;\">")
+ tr("No Device Found") + QLatin1String("</span></p><br/>")
+ msgConnect() + QLatin1String("<br/>")
+ msgAdbListDevices());
connect(m_ui->missingLabel, &QLabel::linkActivated,
this, &AndroidDeviceDialog::showHelp);
connect(m_ui->refreshDevicesButton, &QAbstractButton::clicked,
this, &AndroidDeviceDialog::refreshDeviceList);
connect(m_ui->createAVDButton, &QAbstractButton::clicked,
this, &AndroidDeviceDialog::createAvd);
connect(m_ui->deviceView, &QAbstractItemView::doubleClicked,
this, &QDialog::accept);
connect(&m_futureWatcherAddDevice, &QFutureWatcherBase::finished,
this, &AndroidDeviceDialog::avdAdded);
connect(&m_futureWatcherRefreshDevices, &QFutureWatcherBase::finished,
this, &AndroidDeviceDialog::devicesRefreshed);
connect(m_ui->deviceView->selectionModel(), &QItemSelectionModel::currentChanged,
this, &AndroidDeviceDialog::enableOkayButton);
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Large, this);
m_progressIndicator->attachToWidget(m_ui->deviceView);
if (serialNumber.isEmpty()) {
m_ui->lookingForDevice->setVisible(false);
m_ui->lookingForDeviceCancel->setVisible(false);
} else {
m_ui->lookingForDevice->setVisible(true);
m_ui->lookingForDevice->setText(tr("Looking for default device <b>%1</b>.").arg(serialNumber));
m_ui->lookingForDeviceCancel->setVisible(true);
}
connect(m_ui->lookingForDeviceCancel, &QPushButton::clicked,
this, &AndroidDeviceDialog::defaultDeviceClear);
}
AndroidDeviceDialog::~AndroidDeviceDialog()
{
m_futureWatcherAddDevice.waitForFinished();
m_futureWatcherRefreshDevices.waitForFinished();
delete m_ui;
}
AndroidDeviceInfo AndroidDeviceDialog::defaultDeviceInfo(const QString &serialNumber)
{
AndroidDeviceDialog::updateConnectedDevicesList();
if (serialNumber.isEmpty())
return {};
return Utils::findOrDefault(m_connectedDevices, [serialNumber](const AndroidDeviceInfo &info) {
return info.serialNumber == serialNumber || info.avdname == serialNumber;
});
}
AndroidDeviceInfo AndroidDeviceDialog::showAndGetSelectedDevice()
{
auto dev = defaultDeviceInfo(m_defaultDevice);
if (dev.isValid())
return dev;
refreshDeviceList();
if (exec() == QDialog::Accepted)
return m_model->device(m_ui->deviceView->currentIndex());
return {};
}
bool AndroidDeviceDialog::saveDeviceSelection() const
{
return m_ui->defaultDeviceCheckBox->isChecked();
}
void AndroidDeviceDialog::updateConnectedDevicesList()
{
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig()
.adbToolPath());
}
void AndroidDeviceDialog::refreshDeviceList()
{
m_ui->refreshDevicesButton->setEnabled(false);
m_progressIndicator->show();
m_futureWatcherRefreshDevices.setFuture(m_avdManager->avdList());
}
void AndroidDeviceDialog::devicesRefreshed()
{
m_progressIndicator->hide();
QString serialNumber;
AndroidDeviceInfo::AndroidDeviceType deviceType = AndroidDeviceInfo::Hardware;
QModelIndex currentIndex = m_ui->deviceView->currentIndex();
if (currentIndex.isValid()) { // save currently selected index
AndroidDeviceInfo info = m_model->device(currentIndex);
deviceType = info.type;
serialNumber = deviceType == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname;
}
AndroidDeviceInfoList devices = m_futureWatcherRefreshDevices.result();
QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices,
&AndroidDeviceInfo::avdname);
for (const AndroidDeviceInfo &dev : devices)
if (!startedAvds.contains(dev.avdname))
m_connectedDevices << dev;
m_model->setDevices(m_connectedDevices);
m_ui->deviceView->expand(m_model->index(0, 0));
if (m_model->rowCount() > 1) // we have a incompatible device node
m_ui->deviceView->expand(m_model->index(1, 0));
// Smartly select a index
QModelIndex newIndex;
if (!m_defaultDevice.isEmpty()) {
newIndex = m_model->indexFor(AndroidDeviceInfo::Hardware, m_defaultDevice);
if (!newIndex.isValid())
newIndex = m_model->indexFor(AndroidDeviceInfo::Emulator, m_defaultDevice);
if (!newIndex.isValid()) // not found the default device
defaultDeviceClear();
}
if (!newIndex.isValid() && !m_avdNameFromAdd.isEmpty()) {
newIndex = m_model->indexFor(AndroidDeviceInfo::Emulator, m_avdNameFromAdd);
m_avdNameFromAdd.clear();
}
if (!newIndex.isValid() && !serialNumber.isEmpty())
newIndex = m_model->indexFor(deviceType, serialNumber);
if (!newIndex.isValid() && !m_connectedDevices.isEmpty()) {
AndroidDeviceInfo info = m_connectedDevices.first();
const QString &name = info.type == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname;
newIndex = m_model->indexFor(info.type, name);
}
m_ui->deviceView->setCurrentIndex(newIndex);
m_ui->stackedWidget->setCurrentIndex(m_connectedDevices.isEmpty() ? 1 : 0);
m_ui->refreshDevicesButton->setEnabled(true);
m_connectedDevices.clear();
}
void AndroidDeviceDialog::createAvd()
{
m_ui->createAVDButton->setEnabled(false);
CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, AndroidConfigurations::sdkManager(),
AndroidConfigurations::currentConfig(), m_apiLevel, m_abis);
if (!info.isValid()) {
m_ui->createAVDButton->setEnabled(true);
return;
}
m_futureWatcherAddDevice.setFuture(m_avdManager->createAvd(info));
}
void AndroidDeviceDialog::avdAdded()
{
m_ui->createAVDButton->setEnabled(true);
CreateAvdInfo info = m_futureWatcherAddDevice.result();
if (!info.error.isEmpty()) {
QMessageBox::critical(this, QApplication::translate("AndroidConfig", "Error Creating AVD"), info.error);
return;
}
m_avdNameFromAdd = info.name;
refreshDeviceList();
}
void AndroidDeviceDialog::enableOkayButton()
{
AndroidDeviceModelNode *node = static_cast<AndroidDeviceModelNode *>(m_ui->deviceView->currentIndex().internalPointer());
bool enable = node && (!node->deviceInfo().serialNumber.isEmpty() || !node->deviceInfo().avdname.isEmpty());
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable);
}
void AndroidDeviceDialog::showHelp()
{
QPoint pos = m_ui->missingLabel->pos();
pos = m_ui->missingLabel->parentWidget()->mapToGlobal(pos);
QToolTip::showText(pos, msgConnect() + msgAdbListDevices(), this);
}
void AndroidDeviceDialog::defaultDeviceClear()
{
m_ui->lookingForDevice->setVisible(false);
m_ui->lookingForDeviceCancel->setVisible(false);
m_defaultDevice.clear();
}
#include "androiddevicedialog.moc"

View File

@@ -1,88 +0,0 @@
/****************************************************************************
**
** 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 <QVector>
#include <QDialog>
#include <QFutureWatcher>
#include <QTime>
#include <memory>
QT_BEGIN_NAMESPACE
class QModelIndex;
QT_END_NAMESPACE
namespace Utils { class ProgressIndicator; }
namespace Android {
namespace Internal {
class AndroidAvdManager;
class AndroidDeviceModel;
namespace Ui { class AndroidDeviceDialog; }
class AndroidDeviceDialog : public QDialog
{
Q_OBJECT
public:
explicit AndroidDeviceDialog(int apiLevel, const QStringList &abis,
const QString &serialNumber, QWidget *parent = nullptr);
~AndroidDeviceDialog() override;
AndroidDeviceInfo showAndGetSelectedDevice();
static AndroidDeviceInfo defaultDeviceInfo(const QString &serialNumber);
bool saveDeviceSelection() const;
private:
void refreshDeviceList();
void createAvd();
void showHelp();
void avdAdded();
void devicesRefreshed();
void enableOkayButton();
void defaultDeviceClear();
static void updateConnectedDevicesList();
AndroidDeviceModel *m_model;
Ui::AndroidDeviceDialog *m_ui;
Utils::ProgressIndicator *m_progressIndicator;
int m_apiLevel;
QStringList m_abis;
QString m_avdNameFromAdd;
QString m_defaultDevice;
static QVector<AndroidDeviceInfo> m_connectedDevices;
std::unique_ptr<AndroidAvdManager> m_avdManager;
QFutureWatcher<CreateAvdInfo> m_futureWatcherAddDevice;
QFutureWatcher<AndroidDeviceInfoList> m_futureWatcherRefreshDevices;
};
}
}

View File

@@ -1,215 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Android::Internal::AndroidDeviceDialog</class>
<widget class="QDialog" name="Android::Internal::AndroidDeviceDialog">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>788</width>
<height>466</height>
</rect>
</property>
<property name="windowTitle">
<string>Select Android Device</string>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="6" column="2">
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0" colspan="2">
<widget class="QCheckBox" name="defaultDeviceCheckBox">
<property name="toolTip">
<string>This can be later reset in deployment settings in the Projects mode.</string>
</property>
<property name="text">
<string>Always use this device for architecture %1 for this project</string>
</property>
</widget>
</item>
<item row="0" column="0" colspan="4">
<widget class="QStackedWidget" name="stackedWidget">
<property name="currentIndex">
<number>0</number>
</property>
<widget class="QWidget" name="devicesPage">
<layout class="QGridLayout" name="gridLayout_3">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="1" column="0" colspan="2">
<widget class="QTreeView" name="deviceView">
<property name="minimumSize">
<size>
<width>600</width>
<height>300</height>
</size>
</property>
</widget>
</item>
<item row="2" column="0" colspan="2">
<widget class="QLabel" name="missingLabel">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;aaa&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0057ae;&quot;&gt;My device is missing&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="lookingForDevice">
<property name="text">
<string/>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="lookingForDeviceCancel">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QWidget" name="noDevicesPage">
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QLabel" name="noDeviceFoundLabel">
<property name="text">
<string notr="true">&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="textFormat">
<enum>Qt::RichText</enum>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
<item row="8" column="0" colspan="4">
<widget class="QDialogButtonBox" name="buttonBox">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
<item row="6" column="1">
<widget class="QPushButton" name="createAVDButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Create Android Virtual Device</string>
</property>
</widget>
</item>
<item row="6" column="0">
<widget class="QPushButton" name="refreshDevicesButton">
<property name="sizePolicy">
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Refresh Device List</string>
</property>
</widget>
</item>
</layout>
</widget>
<tabstops>
<tabstop>lookingForDeviceCancel</tabstop>
<tabstop>deviceView</tabstop>
<tabstop>defaultDeviceCheckBox</tabstop>
<tabstop>refreshDevicesButton</tabstop>
<tabstop>createAVDButton</tabstop>
</tabstops>
<resources/>
<connections>
<connection>
<sender>buttonBox</sender>
<signal>accepted()</signal>
<receiver>Android::Internal::AndroidDeviceDialog</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>Android::Internal::AndroidDeviceDialog</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@@ -25,16 +25,17 @@
#include "androidmanager.h"
#include "androidbuildapkstep.h"
#include "androidconstants.h"
#include "androidconfigurations.h"
#include "androidrunconfiguration.h"
#include "androidglobal.h"
#include "androidtoolchain.h"
#include "androiddeployqtstep.h"
#include "androidqtversion.h"
#include "androidavdmanager.h"
#include "androidbuildapkstep.h"
#include "androidconfigurations.h"
#include "androidconstants.h"
#include "androiddeployqtstep.h"
#include "androiddevice.h"
#include "androidglobal.h"
#include "androidqtversion.h"
#include "androidrunconfiguration.h"
#include "androidsdkmanager.h"
#include "androidtoolchain.h"
#include <coreplugin/documentmanager.h>
#include <coreplugin/messagemanager.h>
@@ -552,7 +553,8 @@ void AndroidManager::installQASIPackage(Target *target, const FilePath &packageP
if (appAbis.isEmpty())
return;
const int deviceAPILevel = AndroidManager::minimumSDK(target);
AndroidDeviceInfo info = AndroidConfigurations::showDeviceDialog(target->project(), deviceAPILevel, appAbis);
const IDevice::ConstPtr device = DeviceKitAspect::device(target->kit());
AndroidDeviceInfo info = AndroidDevice::androidDeviceInfoFromIDevice(device.data());
if (!info.isValid()) // aborted
return;

View File

@@ -69,9 +69,7 @@ bool AndroidPotentialKit::isEnabled() const
QList<ProjectExplorer::Kit *> kits = ProjectExplorer::KitManager::kits();
foreach (ProjectExplorer::Kit *kit, kits) {
Utils::Id deviceId = ProjectExplorer::DeviceKitAspect::deviceId(kit);
if (kit->isAutoDetected()
&& deviceId == Utils::Id(Constants::ANDROID_DEVICE_ID)
&& !kit->isSdkProvided()) {
if (kit->isAutoDetected() && !kit->isSdkProvided()) {
return false;
}
}
@@ -121,9 +119,7 @@ void AndroidPotentialKitWidget::recheck()
QList<ProjectExplorer::Kit *> kits = ProjectExplorer::KitManager::kits();
foreach (ProjectExplorer::Kit *kit, kits) {
Utils::Id deviceId = ProjectExplorer::DeviceKitAspect::deviceId(kit);
if (kit->isAutoDetected()
&& deviceId == Utils::Id(Constants::ANDROID_DEVICE_ID)
&& !kit->isSdkProvided()) {
if (kit->isAutoDetected() && !kit->isSdkProvided()) {
setVisible(false);
return;
}

View File

@@ -26,12 +26,13 @@
#include "androidrunner.h"
#include "androidavdmanager.h"
#include "androidconfigurations.h"
#include "androidconstants.h"
#include "androiddeployqtstep.h"
#include "androidconfigurations.h"
#include "androidrunconfiguration.h"
#include "androiddevice.h"
#include "androidmanager.h"
#include "androidavdmanager.h"
#include "androidrunconfiguration.h"
#include "androidrunnerworker.h"
#include <coreplugin/messagemanager.h>
@@ -39,6 +40,7 @@
#include <projectexplorer/projectexplorersettings.h>
#include <projectexplorer/runconfigurationaspects.h>
#include <projectexplorer/target.h>
#include <qtsupport/qtkitinformation.h>
#include <utils/url.h>
#include <QHostAddress>
@@ -185,9 +187,9 @@ void AndroidRunner::launchAVD()
int deviceAPILevel = AndroidManager::minimumSDK(m_target);
QStringList androidAbis = AndroidManager::applicationAbis(m_target);
// Get AVD info.
AndroidDeviceInfo info = AndroidConfigurations::showDeviceDialog(
m_target->project(), deviceAPILevel, androidAbis);
// Get AVD info
const IDevice::ConstPtr device = DeviceKitAspect::device(m_target->kit());
AndroidDeviceInfo info = AndroidDevice::androidDeviceInfoFromIDevice(device.data());
AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber);
emit androidDeviceInfoChanged(info);
if (info.isValid()) {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -29,8 +29,6 @@
<file>images/RunSettings.png</file>
<file>images/EditorSettings.png</file>
<file>images/ProjectDependencies.png</file>
<file>images/MaemoDevice.png</file>
<file>images/Simulator.png</file>
<file>images/devicestatusindicator.png</file>
<file>images/devicestatusindicator@2x.png</file>
<file>images/build.png</file>

View File

@@ -344,10 +344,6 @@ void IconLister::addProjectExplorerIcons()
""},
{QIcon(":/projectexplorer/images/ProjectDependencies.png"), "ProjectDependencies.png", prefix,
""},
{QIcon(":/projectexplorer/images/MaemoDevice.png"), "MaemoDevice.png", prefix,
""},
{QIcon(":/projectexplorer/images/Simulator.png"), "Simulator.png", prefix,
""},
{QIcon(":/projectexplorer/images/targetpanel_bottom.png"), "targetpanel_bottom.png", prefix,
""},
{QIcon(":/projectexplorer/images/unconfigured.png"), "unconfigured.png", prefix,