2013-09-10 19:19:31 +02:00
|
|
|
/****************************************************************************
|
|
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** Copyright (C) 2016 The Qt Company Ltd.
|
|
|
|
|
** Contact: https://www.qt.io/licensing/
|
2013-09-10 19:19:31 +02:00
|
|
|
**
|
|
|
|
|
** 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
|
2016-01-15 14:57:40 +01:00
|
|
|
** 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.
|
2013-09-10 19:19:31 +02:00
|
|
|
**
|
2016-01-15 14:57:40 +01:00
|
|
|
** 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.
|
2013-09-10 19:19:31 +02:00
|
|
|
**
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include "androiddevicedialog.h"
|
|
|
|
|
#include "androidmanager.h"
|
2017-04-03 11:11:17 +02:00
|
|
|
#include "androidavdmanager.h"
|
2017-08-18 08:22:34 +02:00
|
|
|
#include "avddialog.h"
|
2013-09-10 19:19:31 +02:00
|
|
|
#include "ui_androiddevicedialog.h"
|
|
|
|
|
|
2015-04-10 18:08:51 +02:00
|
|
|
#include <utils/environment.h>
|
|
|
|
|
#include <utils/progressindicator.h>
|
2015-04-23 16:25:44 +02:00
|
|
|
#include <utils/algorithm.h>
|
2015-04-10 18:08:51 +02:00
|
|
|
|
2014-04-11 13:31:01 +02:00
|
|
|
#include <QMessageBox>
|
2013-09-10 19:19:31 +02:00
|
|
|
#include <QPainter>
|
|
|
|
|
#include <QStyledItemDelegate>
|
2014-03-06 17:35:11 +01:00
|
|
|
#include <QToolTip>
|
2013-09-10 19:19:31 +02:00
|
|
|
|
|
|
|
|
using namespace Android;
|
|
|
|
|
using namespace Android::Internal;
|
|
|
|
|
|
|
|
|
|
namespace Android {
|
|
|
|
|
namespace Internal {
|
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
{
|
2015-07-23 14:31:43 +03:00
|
|
|
Q_OBJECT
|
2013-09-10 19:19:31 +02:00
|
|
|
public:
|
|
|
|
|
AndroidDeviceModelDelegate(QObject * parent = 0)
|
|
|
|
|
: QStyledItemDelegate(parent)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~AndroidDeviceModelDelegate()
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
|
|
|
|
{
|
2016-01-21 15:00:33 +01:00
|
|
|
QStyleOptionViewItem opt = option;
|
2013-09-10 19:19:31 +02:00
|
|
|
initStyleOption(&opt, index);
|
|
|
|
|
painter->save();
|
|
|
|
|
|
|
|
|
|
AndroidDeviceModelNode *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.background().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);
|
|
|
|
|
|
2013-10-02 12:30:19 +02:00
|
|
|
if (!node->displayName().isEmpty()) { // Title
|
2013-09-10 19:19:31 +02:00
|
|
|
// 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();
|
2013-10-02 12:30:19 +02:00
|
|
|
painter->drawText(6, top, node->displayName());
|
2013-09-10 19:19:31 +02:00
|
|
|
} 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
|
2015-04-23 16:25:44 +02:00
|
|
|
QString topLeft;
|
2013-09-10 19:19:31 +02:00
|
|
|
if (device.type == AndroidDeviceInfo::Hardware)
|
2013-12-16 20:19:07 +01:00
|
|
|
topLeft = AndroidConfigurations::currentConfig().getProductModel(device.serialNumber);
|
2015-04-23 16:25:44 +02:00
|
|
|
else
|
|
|
|
|
topLeft = device.avdname;
|
2013-09-10 19:19:31 +02:00
|
|
|
painter->drawText(size + 12, 2 + opt.rect.top() + fm.ascent(), topLeft);
|
|
|
|
|
|
2015-04-10 16:46:30 +02:00
|
|
|
|
2013-09-10 19:19:31 +02:00
|
|
|
// topRight
|
2015-04-10 16:46:30 +02:00
|
|
|
auto drawTopRight = [&](const QString text, const QFontMetrics &fm) {
|
|
|
|
|
painter->drawText(opt.rect.right() - fm.width(text) - 6 , 2 + opt.rect.top() + fm.ascent(), text);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (device.type == AndroidDeviceInfo::Hardware) {
|
|
|
|
|
drawTopRight(device.serialNumber, fm);
|
|
|
|
|
} else {
|
2015-04-23 16:25:44 +02:00
|
|
|
AndroidConfig::OpenGl openGl = AndroidConfigurations::currentConfig().getOpenGLEnabled(device.avdname);
|
2015-04-10 16:46:30 +02:00
|
|
|
if (openGl == AndroidConfig::OpenGl::Enabled) {
|
2015-04-29 14:46:16 +02:00
|
|
|
drawTopRight(tr("OpenGL enabled"), fm);
|
2015-04-10 16:46:30 +02:00
|
|
|
} 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);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-09-10 19:19:31 +02:00
|
|
|
|
|
|
|
|
// 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(" ");
|
2014-08-23 01:19:53 +02:00
|
|
|
lineText += AndroidDeviceDialog::tr("ABI:") + device.cpuAbi.join(QLatin1Char(' '));
|
2013-09-10 19:19:31 +02:00
|
|
|
} else {
|
|
|
|
|
lineText = node->incompatibleReason();
|
2015-02-16 17:18:11 +01:00
|
|
|
QFont f = painter->font();
|
|
|
|
|
f.setBold(true);
|
|
|
|
|
painter->setFont(f);
|
2013-09-10 19:19:31 +02:00
|
|
|
}
|
|
|
|
|
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
|
|
|
|
|
{
|
2016-01-21 15:00:33 +01:00
|
|
|
QStyleOptionViewItem opt = option;
|
2013-09-10 19:19:31 +02:00
|
|
|
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
|
|
|
|
|
{
|
2015-07-23 14:31:43 +03:00
|
|
|
Q_OBJECT
|
2013-09-10 19:19:31 +02:00
|
|
|
public:
|
2017-09-04 15:21:16 -07:00
|
|
|
AndroidDeviceModel(int apiLevel, const QString &abi);
|
2013-09-10 19:19:31 +02:00
|
|
|
QModelIndex index(int row, int column,
|
|
|
|
|
const QModelIndex &parent = QModelIndex()) const;
|
|
|
|
|
QModelIndex parent(const QModelIndex &child) const;
|
|
|
|
|
int rowCount(const QModelIndex &parent = QModelIndex()) const;
|
|
|
|
|
int columnCount(const QModelIndex &parent = QModelIndex()) const;
|
|
|
|
|
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
|
|
|
|
|
Qt::ItemFlags flags(const QModelIndex &index) const;
|
|
|
|
|
|
|
|
|
|
AndroidDeviceInfo device(QModelIndex index);
|
|
|
|
|
void setDevices(const QVector<AndroidDeviceInfo> &devices);
|
|
|
|
|
|
2015-04-23 16:25:44 +02:00
|
|
|
QModelIndex indexFor(AndroidDeviceInfo::AndroidDeviceType type, const QString &serial);
|
2013-09-10 19:19:31 +02:00
|
|
|
private:
|
|
|
|
|
int m_apiLevel;
|
|
|
|
|
QString m_abi;
|
|
|
|
|
AndroidDeviceModelNode *m_root;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/////////////////
|
|
|
|
|
// AndroidDeviceModel
|
|
|
|
|
/////////////////
|
2017-09-04 15:21:16 -07:00
|
|
|
AndroidDeviceModel::AndroidDeviceModel(int apiLevel, const QString &abi)
|
|
|
|
|
: m_apiLevel(apiLevel), m_abi(abi), m_root(0)
|
2013-09-10 19:19:31 +02:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AndroidDeviceModelNode *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();
|
|
|
|
|
AndroidDeviceModelNode *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();
|
|
|
|
|
AndroidDeviceModelNode *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();
|
|
|
|
|
AndroidDeviceModelNode *node = static_cast<AndroidDeviceModelNode *>(index.internalPointer());
|
|
|
|
|
if (!node)
|
|
|
|
|
return QVariant();
|
|
|
|
|
return node->deviceInfo().serialNumber;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Qt::ItemFlags AndroidDeviceModel::flags(const QModelIndex &index) const
|
|
|
|
|
{
|
|
|
|
|
AndroidDeviceModelNode *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)
|
|
|
|
|
{
|
|
|
|
|
AndroidDeviceModelNode *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(0, QString());
|
|
|
|
|
|
2013-09-25 16:45:18 +02:00
|
|
|
AndroidDeviceModelNode *compatibleDevices = new AndroidDeviceModelNode(m_root, AndroidDeviceDialog::tr("Compatible devices"));
|
2013-09-10 19:19:31 +02:00
|
|
|
AndroidDeviceModelNode *incompatibleDevices = 0; // created on demand
|
|
|
|
|
foreach (const AndroidDeviceInfo &device, devices) {
|
|
|
|
|
QString error;
|
2014-03-10 15:18:46 +01:00
|
|
|
if (device.state == AndroidDeviceInfo::UnAuthorizedState) {
|
2013-09-25 16:45:18 +02:00
|
|
|
error = AndroidDeviceDialog::tr("Unauthorized. Please check the confirmation dialog on your device %1.")
|
|
|
|
|
.arg(device.serialNumber);
|
2014-03-10 15:18:46 +01:00
|
|
|
}else if (device.state == AndroidDeviceInfo::OfflineState) {
|
|
|
|
|
error = AndroidDeviceDialog::tr("Offline. Please check the state of your device %1.")
|
|
|
|
|
.arg(device.serialNumber);
|
2013-09-10 19:19:31 +02:00
|
|
|
} else if (!device.cpuAbi.contains(m_abi)) {
|
2013-09-25 16:45:18 +02:00
|
|
|
error = AndroidDeviceDialog::tr("ABI is incompatible, device supports ABIs: %1.")
|
2014-08-23 01:19:53 +02:00
|
|
|
.arg(device.cpuAbi.join(QLatin1Char(' ')));
|
2013-09-10 19:19:31 +02:00
|
|
|
} else if (device.sdk < m_apiLevel) {
|
2013-09-25 16:45:18 +02:00
|
|
|
error = AndroidDeviceDialog::tr("API Level of device is: %1.")
|
2013-09-10 19:19:31 +02:00
|
|
|
.arg(device.sdk);
|
|
|
|
|
} else {
|
|
|
|
|
new AndroidDeviceModelNode(compatibleDevices, device);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!incompatibleDevices)
|
2013-09-25 16:45:18 +02:00
|
|
|
incompatibleDevices = new AndroidDeviceModelNode(m_root, AndroidDeviceDialog::tr("Incompatible devices"));
|
2013-09-10 19:19:31 +02:00
|
|
|
new AndroidDeviceModelNode(incompatibleDevices, device, error);
|
|
|
|
|
}
|
|
|
|
|
endResetModel();
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-23 16:25:44 +02:00
|
|
|
QModelIndex AndroidDeviceModel::indexFor(AndroidDeviceInfo::AndroidDeviceType type, const QString &serial)
|
2013-09-10 19:19:31 +02:00
|
|
|
{
|
|
|
|
|
foreach (AndroidDeviceModelNode *topLevelNode, m_root->children()) {
|
|
|
|
|
QList<AndroidDeviceModelNode *> deviceNodes = topLevelNode->children();
|
|
|
|
|
for (int i = 0; i < deviceNodes.size(); ++i) {
|
2015-04-23 16:25:44 +02:00
|
|
|
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))
|
2013-09-10 19:19:31 +02:00
|
|
|
return createIndex(i, 0, deviceNodes.at(i));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return QModelIndex();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////
|
|
|
|
|
// AndroidDeviceDialog
|
|
|
|
|
/////////////////
|
2014-03-19 09:32:06 +01:00
|
|
|
|
|
|
|
|
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>");
|
|
|
|
|
}
|
|
|
|
|
|
2017-09-04 15:21:16 -07:00
|
|
|
AndroidDeviceDialog::AndroidDeviceDialog(int apiLevel, const QString &abi,
|
2015-04-29 15:21:37 +02:00
|
|
|
const QString &serialNumber, QWidget *parent) :
|
2013-09-10 19:19:31 +02:00
|
|
|
QDialog(parent),
|
2017-09-04 15:21:16 -07:00
|
|
|
m_model(new AndroidDeviceModel(apiLevel, abi)),
|
2013-09-10 19:19:31 +02:00
|
|
|
m_ui(new Ui::AndroidDeviceDialog),
|
|
|
|
|
m_apiLevel(apiLevel),
|
2015-04-29 15:21:37 +02:00
|
|
|
m_abi(abi),
|
2017-03-30 14:43:13 +02:00
|
|
|
m_defaultDevice(serialNumber),
|
2017-04-03 11:11:17 +02:00
|
|
|
m_avdManager(new AndroidAvdManager)
|
2013-09-10 19:19:31 +02:00
|
|
|
{
|
|
|
|
|
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);
|
2013-10-02 12:30:19 +02:00
|
|
|
m_ui->deviceView->setExpandsOnDoubleClick(false);
|
2013-09-10 19:19:31 +02:00
|
|
|
|
2015-12-01 12:28:28 +01:00
|
|
|
m_ui->defaultDeviceCheckBox->setText(tr("Always use this device for architecture %1 for this project").arg(abi));
|
2013-09-10 19:19:31 +02:00
|
|
|
|
2014-03-19 09:32:06 +01:00
|
|
|
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());
|
2016-06-26 22:52:59 +03:00
|
|
|
connect(m_ui->missingLabel, &QLabel::linkActivated,
|
|
|
|
|
this, &AndroidDeviceDialog::showHelp);
|
2014-03-06 17:35:11 +01:00
|
|
|
|
2016-06-26 22:52:59 +03:00
|
|
|
connect(m_ui->refreshDevicesButton, &QAbstractButton::clicked,
|
|
|
|
|
this, &AndroidDeviceDialog::refreshDeviceList);
|
2013-09-10 19:19:31 +02:00
|
|
|
|
2016-06-26 22:52:59 +03:00
|
|
|
connect(m_ui->createAVDButton, &QAbstractButton::clicked,
|
|
|
|
|
this, &AndroidDeviceDialog::createAvd);
|
|
|
|
|
connect(m_ui->deviceView, &QAbstractItemView::doubleClicked,
|
|
|
|
|
this, &QDialog::accept);
|
2013-09-10 19:19:31 +02:00
|
|
|
|
2016-06-26 22:52:59 +03:00
|
|
|
connect(&m_futureWatcherAddDevice, &QFutureWatcherBase::finished,
|
|
|
|
|
this, &AndroidDeviceDialog::avdAdded);
|
2015-04-10 18:08:51 +02:00
|
|
|
connect(&m_futureWatcherRefreshDevices, &QFutureWatcherBase::finished,
|
|
|
|
|
this, &AndroidDeviceDialog::devicesRefreshed);
|
2014-04-11 13:31:01 +02:00
|
|
|
|
2015-04-10 18:08:51 +02:00
|
|
|
connect(m_ui->deviceView->selectionModel(), &QItemSelectionModel::currentChanged,
|
|
|
|
|
this, &AndroidDeviceDialog::enableOkayButton);
|
|
|
|
|
|
|
|
|
|
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);
|
|
|
|
|
|
2017-07-25 13:51:36 +02:00
|
|
|
m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicatorSize::Large, this);
|
2015-04-10 18:08:51 +02:00
|
|
|
m_progressIndicator->attachToWidget(m_ui->deviceView);
|
2015-04-29 15:21:37 +02:00
|
|
|
|
|
|
|
|
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);
|
2015-12-03 18:27:43 +01:00
|
|
|
|
2015-12-04 10:03:45 +02:00
|
|
|
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString());
|
2013-09-10 19:19:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AndroidDeviceDialog::~AndroidDeviceDialog()
|
|
|
|
|
{
|
2015-04-10 18:08:51 +02:00
|
|
|
m_futureWatcherAddDevice.waitForFinished();
|
|
|
|
|
m_futureWatcherRefreshDevices.waitForFinished();
|
2013-09-10 19:19:31 +02:00
|
|
|
delete m_ui;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AndroidDeviceInfo AndroidDeviceDialog::device()
|
|
|
|
|
{
|
2015-12-04 10:03:45 +02:00
|
|
|
if (!m_defaultDevice.isEmpty()) {
|
|
|
|
|
auto device = std::find_if(m_connectedDevices.begin(), m_connectedDevices.end(), [this](const AndroidDeviceInfo& info) {
|
|
|
|
|
return info.serialNumber == m_defaultDevice ||
|
|
|
|
|
info.avdname == m_defaultDevice;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (device != m_connectedDevices.end())
|
|
|
|
|
return *device;
|
|
|
|
|
m_defaultDevice.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
refreshDeviceList();
|
|
|
|
|
|
|
|
|
|
if (exec() == QDialog::Accepted)
|
2013-09-10 19:19:31 +02:00
|
|
|
return m_model->device(m_ui->deviceView->currentIndex());
|
|
|
|
|
return AndroidDeviceInfo();
|
|
|
|
|
}
|
|
|
|
|
|
2015-12-04 10:03:45 +02:00
|
|
|
bool AndroidDeviceDialog::saveDeviceSelection() const
|
2013-09-10 19:19:31 +02:00
|
|
|
{
|
|
|
|
|
return m_ui->defaultDeviceCheckBox->isChecked();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidDeviceDialog::refreshDeviceList()
|
|
|
|
|
{
|
2015-04-10 18:08:51 +02:00
|
|
|
m_ui->refreshDevicesButton->setEnabled(false);
|
2015-12-01 12:33:29 +01:00
|
|
|
m_progressIndicator->show();
|
2015-12-04 10:03:45 +02:00
|
|
|
m_connectedDevices = AndroidConfig::connectedDevices(AndroidConfigurations::currentConfig().adbToolPath().toString());
|
2017-04-03 11:11:17 +02:00
|
|
|
m_futureWatcherRefreshDevices.setFuture(m_avdManager->avdList());
|
2015-04-10 18:08:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidDeviceDialog::devicesRefreshed()
|
|
|
|
|
{
|
|
|
|
|
m_progressIndicator->hide();
|
|
|
|
|
QString serialNumber;
|
2015-06-11 18:00:31 +03:00
|
|
|
AndroidDeviceInfo::AndroidDeviceType deviceType = AndroidDeviceInfo::Hardware;
|
2015-04-29 15:21:37 +02:00
|
|
|
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;
|
2015-04-10 18:08:51 +02:00
|
|
|
}
|
|
|
|
|
|
2017-04-03 11:11:17 +02:00
|
|
|
AndroidDeviceInfoList devices = m_futureWatcherRefreshDevices.result();
|
2015-12-04 10:03:45 +02:00
|
|
|
QSet<QString> startedAvds = Utils::transform<QSet>(m_connectedDevices,
|
|
|
|
|
[] (const AndroidDeviceInfo &info) {
|
|
|
|
|
return info.avdname;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (const AndroidDeviceInfo &dev : devices)
|
|
|
|
|
if (!startedAvds.contains(dev.avdname))
|
|
|
|
|
m_connectedDevices << dev;
|
|
|
|
|
|
|
|
|
|
m_model->setDevices(m_connectedDevices);
|
2013-09-10 19:19:31 +02:00
|
|
|
|
|
|
|
|
m_ui->deviceView->expand(m_model->index(0, 0));
|
2013-10-02 12:30:19 +02:00
|
|
|
if (m_model->rowCount() > 1) // we have a incompatible device node
|
|
|
|
|
m_ui->deviceView->expand(m_model->index(1, 0));
|
2013-09-10 19:19:31 +02:00
|
|
|
|
|
|
|
|
// Smartly select a index
|
|
|
|
|
QModelIndex newIndex;
|
2015-04-29 15:21:37 +02:00
|
|
|
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())
|
2015-04-23 16:25:44 +02:00
|
|
|
newIndex = m_model->indexFor(deviceType, serialNumber);
|
2013-09-10 19:19:31 +02:00
|
|
|
|
2015-12-04 10:03:45 +02:00
|
|
|
if (!newIndex.isValid() && !m_connectedDevices.isEmpty()) {
|
|
|
|
|
AndroidDeviceInfo info = m_connectedDevices.first();
|
2015-04-23 16:25:44 +02:00
|
|
|
const QString &name = info.type == AndroidDeviceInfo::Hardware ? info.serialNumber : info.avdname;
|
|
|
|
|
newIndex = m_model->indexFor(info.type, name);
|
|
|
|
|
}
|
2013-09-10 19:19:31 +02:00
|
|
|
|
|
|
|
|
m_ui->deviceView->setCurrentIndex(newIndex);
|
2014-03-06 17:35:11 +01:00
|
|
|
|
2015-12-04 10:03:45 +02:00
|
|
|
m_ui->stackedWidget->setCurrentIndex(m_connectedDevices.isEmpty() ? 1 : 0);
|
2015-04-10 18:08:51 +02:00
|
|
|
|
|
|
|
|
m_ui->refreshDevicesButton->setEnabled(true);
|
2015-12-04 10:03:45 +02:00
|
|
|
m_connectedDevices.clear();
|
2013-09-10 19:19:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidDeviceDialog::createAvd()
|
|
|
|
|
{
|
2014-04-11 13:31:01 +02:00
|
|
|
m_ui->createAVDButton->setEnabled(false);
|
2017-08-18 08:22:34 +02:00
|
|
|
CreateAvdInfo info = AvdDialog::gatherCreateAVDInfo(this, AndroidConfigurations::sdkManager(),
|
|
|
|
|
m_apiLevel, m_abi);
|
2014-04-11 13:31:01 +02:00
|
|
|
|
2017-08-18 08:22:34 +02:00
|
|
|
if (!info.isValid()) {
|
2014-04-11 13:31:01 +02:00
|
|
|
m_ui->createAVDButton->setEnabled(true);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-03 11:11:17 +02:00
|
|
|
m_futureWatcherAddDevice.setFuture(m_avdManager->createAvd(info));
|
2014-04-11 13:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidDeviceDialog::avdAdded()
|
|
|
|
|
{
|
|
|
|
|
m_ui->createAVDButton->setEnabled(true);
|
2017-08-18 08:22:34 +02:00
|
|
|
CreateAvdInfo info = m_futureWatcherAddDevice.result();
|
2014-04-11 13:31:01 +02:00
|
|
|
if (!info.error.isEmpty()) {
|
|
|
|
|
QMessageBox::critical(this, QApplication::translate("AndroidConfig", "Error Creating AVD"), info.error);
|
2013-09-10 19:19:31 +02:00
|
|
|
return;
|
2014-04-11 13:31:01 +02:00
|
|
|
}
|
|
|
|
|
|
2015-04-23 16:25:44 +02:00
|
|
|
m_avdNameFromAdd = info.name;
|
2013-09-10 19:19:31 +02:00
|
|
|
refreshDeviceList();
|
2015-04-10 18:08:51 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void AndroidDeviceDialog::enableOkayButton()
|
|
|
|
|
{
|
|
|
|
|
AndroidDeviceModelNode *node = static_cast<AndroidDeviceModelNode *>(m_ui->deviceView->currentIndex().internalPointer());
|
2015-04-23 16:25:44 +02:00
|
|
|
bool enable = node && (!node->deviceInfo().serialNumber.isEmpty() || !node->deviceInfo().avdname.isEmpty());
|
2015-04-10 18:08:51 +02:00
|
|
|
m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(enable);
|
2013-09-10 19:19:31 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Does not work.
|
|
|
|
|
void AndroidDeviceDialog::clickedOnView(const QModelIndex &idx)
|
|
|
|
|
{
|
|
|
|
|
if (idx.isValid()) {
|
|
|
|
|
AndroidDeviceModelNode *node = static_cast<AndroidDeviceModelNode *>(idx.internalPointer());
|
|
|
|
|
if (!node->displayName().isEmpty()) {
|
|
|
|
|
if (m_ui->deviceView->isExpanded(idx))
|
|
|
|
|
m_ui->deviceView->collapse(idx);
|
|
|
|
|
else
|
|
|
|
|
m_ui->deviceView->expand(idx);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-03-06 17:35:11 +01:00
|
|
|
|
|
|
|
|
void AndroidDeviceDialog::showHelp()
|
|
|
|
|
{
|
|
|
|
|
QPoint pos = m_ui->missingLabel->pos();
|
|
|
|
|
pos = m_ui->missingLabel->parentWidget()->mapToGlobal(pos);
|
2014-03-19 09:32:06 +01:00
|
|
|
QToolTip::showText(pos, msgConnect() + msgAdbListDevices(), this);
|
2014-03-06 17:35:11 +01:00
|
|
|
}
|
2015-04-29 15:21:37 +02:00
|
|
|
|
|
|
|
|
void AndroidDeviceDialog::defaultDeviceClear()
|
|
|
|
|
{
|
|
|
|
|
m_ui->lookingForDevice->setVisible(false);
|
|
|
|
|
m_ui->lookingForDeviceCancel->setVisible(false);
|
|
|
|
|
m_defaultDevice.clear();
|
|
|
|
|
}
|
2015-07-23 14:31:43 +03:00
|
|
|
|
|
|
|
|
#include "androiddevicedialog.moc"
|