diff options
author | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2021-08-22 01:51:57 +0300 |
---|---|---|
committer | Assam Boudjelthia <assam.boudjelthia@qt.io> | 2021-09-21 15:33:40 +0000 |
commit | 96255208a53532906c1b0c3130dc53776efa3a71 (patch) | |
tree | 5aa0e851aefbdc542fa5db9cd1c71ab002bab452 /src | |
parent | 15b6eaa47b348efaae4756cd2a095dc78273337d (diff) | |
download | qt-creator-96255208a53532906c1b0c3130dc53776efa3a71.tar.gz |
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>
Diffstat (limited to 'src')
19 files changed, 417 insertions, 1071 deletions
diff --git a/src/plugins/android/CMakeLists.txt b/src/plugins/android/CMakeLists.txt index bd63886f9a..12885d3699 100644 --- a/src/plugins/android/CMakeLists.txt +++ b/src/plugins/android/CMakeLists.txt @@ -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 diff --git a/src/plugins/android/android.pro b/src/plugins/android/android.pro index 7bdd5539c9..4e0af0ec77 100644 --- a/src/plugins/android/android.pro +++ b/src/plugins/android/android.pro @@ -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 diff --git a/src/plugins/android/android.qbs b/src/plugins/android/android.qbs index 7dbe73b1d9..52c67e4503 100644 --- a/src/plugins/android/android.qbs +++ b/src/plugins/android/android.qbs @@ -35,9 +35,6 @@ Project { "androiddeployqtstep.h", "androiddebugsupport.cpp", "androiddebugsupport.h", - "androiddevicedialog.cpp", - "androiddevicedialog.h", - "androiddevicedialog.ui", "androiddevice.cpp", "androiddevice.h", "androiddeviceinfo.cpp", diff --git a/src/plugins/android/androidconfigurations.cpp b/src/plugins/android/androidconfigurations.cpp index ff16e3f0be..f1eb6379d6 100644 --- a/src/plugins/android/androidconfigurations.cpp +++ b/src/plugins/android/androidconfigurations.cpp @@ -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; diff --git a/src/plugins/android/androidconfigurations.h b/src/plugins/android/androidconfigurations.h index dc9d3fd6be..7071fe5357 100644 --- a/src/plugins/android/androidconfigurations.h +++ b/src/plugins/android/androidconfigurations.h @@ -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; }; diff --git a/src/plugins/android/androidconstants.h b/src/plugins/android/androidconstants.h index 01350cd718..df8c821f98 100644 --- a/src/plugins/android/androidconstants.h +++ b/src/plugins/android/androidconstants.h @@ -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 diff --git a/src/plugins/android/androiddeployqtstep.cpp b/src/plugins/android/androiddeployqtstep.cpp index 9e9c85b70f..12d3b3e651 100644 --- a/src/plugins/android/androiddeployqtstep.cpp +++ b/src/plugins/android/androiddeployqtstep.cpp @@ -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); diff --git a/src/plugins/android/androiddevice.cpp b/src/plugins/android/androiddevice.cpp index 16ff682824..6c24978b53 100644 --- a/src/plugins/android/androiddevice.cpp +++ b/src/plugins/android/androiddevice.cpp @@ -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); + + 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; +} - setDeviceState(DeviceReadyToUse); +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(); + })); + } +} -// Factory +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) { diff --git a/src/plugins/android/androiddevice.h b/src/plugins/android/androiddevice.h index 4fea1956c3..b221522307 100644 --- a/src/plugins/android/androiddevice.h +++ b/src/plugins/android/androiddevice.h @@ -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 diff --git a/src/plugins/android/androiddevicedialog.cpp b/src/plugins/android/androiddevicedialog.cpp deleted file mode 100644 index 9e33808ebb..0000000000 --- a/src/plugins/android/androiddevicedialog.cpp +++ /dev/null @@ -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 "adb devices".</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" diff --git a/src/plugins/android/androiddevicedialog.h b/src/plugins/android/androiddevicedialog.h deleted file mode 100644 index 33bd2871e9..0000000000 --- a/src/plugins/android/androiddevicedialog.h +++ /dev/null @@ -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; -}; - -} -} diff --git a/src/plugins/android/androiddevicedialog.ui b/src/plugins/android/androiddevicedialog.ui deleted file mode 100644 index aac84c80ec..0000000000 --- a/src/plugins/android/androiddevicedialog.ui +++ /dev/null @@ -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><html><head/><body><p><a href="aaa"><span style=" text-decoration: underline; color:#0057ae;">My device is missing</span></a></p></body></html></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"><html><head/><body><p><br/></p></body></html></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> diff --git a/src/plugins/android/androidmanager.cpp b/src/plugins/android/androidmanager.cpp index d5177de681..7d181c405b 100644 --- a/src/plugins/android/androidmanager.cpp +++ b/src/plugins/android/androidmanager.cpp @@ -25,16 +25,17 @@ #include "androidmanager.h" +#include "androidavdmanager.h" #include "androidbuildapkstep.h" -#include "androidconstants.h" #include "androidconfigurations.h" -#include "androidrunconfiguration.h" -#include "androidglobal.h" -#include "androidtoolchain.h" +#include "androidconstants.h" #include "androiddeployqtstep.h" +#include "androiddevice.h" +#include "androidglobal.h" #include "androidqtversion.h" -#include "androidavdmanager.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; diff --git a/src/plugins/android/androidpotentialkit.cpp b/src/plugins/android/androidpotentialkit.cpp index efb266a27a..b2d5bbe7cb 100644 --- a/src/plugins/android/androidpotentialkit.cpp +++ b/src/plugins/android/androidpotentialkit.cpp @@ -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; } diff --git a/src/plugins/android/androidrunner.cpp b/src/plugins/android/androidrunner.cpp index 1682d882b4..c0266e1d71 100644 --- a/src/plugins/android/androidrunner.cpp +++ b/src/plugins/android/androidrunner.cpp @@ -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()) { diff --git a/src/plugins/projectexplorer/images/MaemoDevice.png b/src/plugins/projectexplorer/images/MaemoDevice.png Binary files differdeleted file mode 100644 index 51886524cc..0000000000 --- a/src/plugins/projectexplorer/images/MaemoDevice.png +++ /dev/null diff --git a/src/plugins/projectexplorer/images/Simulator.png b/src/plugins/projectexplorer/images/Simulator.png Binary files differdeleted file mode 100644 index 447054bac8..0000000000 --- a/src/plugins/projectexplorer/images/Simulator.png +++ /dev/null diff --git a/src/plugins/projectexplorer/projectexplorer.qrc b/src/plugins/projectexplorer/projectexplorer.qrc index b09c7e26da..2c48d30a85 100644 --- a/src/plugins/projectexplorer/projectexplorer.qrc +++ b/src/plugins/projectexplorer/projectexplorer.qrc @@ -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> diff --git a/src/tools/iconlister/iconlister.cpp b/src/tools/iconlister/iconlister.cpp index 17ebbc0306..a0283bacb2 100644 --- a/src/tools/iconlister/iconlister.cpp +++ b/src/tools/iconlister/iconlister.cpp @@ -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, |