forked from qt-creator/qt-creator
Android: Let user specify shell commands
Let user specify list of shell commands to run before app starts and after app quits. Change-Id: I9794fb96180530ca6c28ce6581fda51a25be28d4 Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
198
src/plugins/android/adbcommandswidget.cpp
Normal file
198
src/plugins/android/adbcommandswidget.cpp
Normal file
@@ -0,0 +1,198 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 "adbcommandswidget.h"
|
||||
#include "ui_adbcommandswidget.h"
|
||||
|
||||
#include "utils/utilsicons.h"
|
||||
|
||||
#include <QGroupBox>
|
||||
#include <QItemSelectionModel>
|
||||
#include <QShortcut>
|
||||
#include <QStringListModel>
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
|
||||
using namespace std::placeholders;
|
||||
|
||||
const char defaultCommand[] = "echo \"shell command\"";
|
||||
|
||||
static void swapData(QStringListModel *model, const QModelIndex &srcIndex,
|
||||
const QModelIndex &destIndex)
|
||||
{
|
||||
if (model) {
|
||||
QVariant data = model->data(destIndex, Qt::EditRole); // QTBUG-55078
|
||||
model->setData(destIndex, model->data(srcIndex, Qt::EditRole));
|
||||
model->setData(srcIndex, data);
|
||||
}
|
||||
}
|
||||
|
||||
class AdbCommandsWidgetPrivate
|
||||
{
|
||||
public:
|
||||
AdbCommandsWidgetPrivate(const AdbCommandsWidget &parent, QWidget *parentWidget);
|
||||
~AdbCommandsWidgetPrivate();
|
||||
|
||||
private:
|
||||
void addString(const QString &str);
|
||||
void onAddButton();
|
||||
void onMoveUpButton();
|
||||
void onMoveDownButton();
|
||||
void onRemove();
|
||||
void onCurrentIndexChanged(const QModelIndex &newIndex, const QModelIndex &prevIndex);
|
||||
|
||||
const AdbCommandsWidget &m_parent;
|
||||
QGroupBox *m_rootWidget = nullptr;
|
||||
Ui::AdbCommandsWidget *m_ui = nullptr;
|
||||
QStringListModel *m_stringModel = nullptr;
|
||||
friend class AdbCommandsWidget;
|
||||
};
|
||||
|
||||
AdbCommandsWidget::AdbCommandsWidget(QWidget *parent) :
|
||||
QObject(parent),
|
||||
d(new AdbCommandsWidgetPrivate(*this, parent))
|
||||
{
|
||||
}
|
||||
|
||||
AdbCommandsWidget::~AdbCommandsWidget()
|
||||
{
|
||||
}
|
||||
|
||||
QStringList AdbCommandsWidget::commandsList() const
|
||||
{
|
||||
return d->m_stringModel->stringList();
|
||||
}
|
||||
|
||||
QWidget *AdbCommandsWidget::widget() const
|
||||
{
|
||||
return d->m_rootWidget;
|
||||
}
|
||||
|
||||
void AdbCommandsWidget::setTitleText(const QString &title)
|
||||
{
|
||||
d->m_rootWidget->setTitle(title);
|
||||
}
|
||||
|
||||
void AdbCommandsWidget::setCommandList(const QStringList &commands)
|
||||
{
|
||||
d->m_stringModel->setStringList(commands);
|
||||
}
|
||||
|
||||
AdbCommandsWidgetPrivate::AdbCommandsWidgetPrivate(const AdbCommandsWidget &parent,
|
||||
QWidget *parentWidget):
|
||||
m_parent(parent),
|
||||
m_rootWidget(new QGroupBox(parentWidget)),
|
||||
m_ui(new Ui::AdbCommandsWidget),
|
||||
m_stringModel(new QStringListModel)
|
||||
{
|
||||
m_ui->setupUi(m_rootWidget);
|
||||
m_ui->addButton->setIcon(Utils::Icons::PLUS.icon());
|
||||
m_ui->removeButton->setIcon(Utils::Icons::MINUS.icon());
|
||||
m_ui->moveUpButton->setIcon(Utils::Icons::ARROW_UP.icon());
|
||||
m_ui->moveDownButton->setIcon(Utils::Icons::ARROW_DOWN.icon());
|
||||
|
||||
auto deleteShortcut = new QShortcut(QKeySequence(QKeySequence::Delete), m_ui->commandsView);
|
||||
deleteShortcut->setContext(Qt::WidgetShortcut);
|
||||
QObject::connect(deleteShortcut, &QShortcut::activated,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onRemove, this));
|
||||
|
||||
QObject::connect(m_ui->addButton, &QToolButton::clicked,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onAddButton, this));
|
||||
QObject::connect(m_ui->removeButton, &QToolButton::clicked,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onRemove, this));
|
||||
QObject::connect(m_ui->moveUpButton, &QToolButton::clicked,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onMoveUpButton, this));
|
||||
QObject::connect(m_ui->moveDownButton, &QToolButton::clicked,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onMoveDownButton, this));
|
||||
|
||||
m_ui->commandsView->setModel(m_stringModel);
|
||||
QObject::connect(m_stringModel, &QStringListModel::dataChanged,
|
||||
&m_parent, &AdbCommandsWidget::commandsChanged);
|
||||
QObject::connect(m_stringModel, &QStringListModel::rowsRemoved,
|
||||
&m_parent, &AdbCommandsWidget::commandsChanged);
|
||||
QObject::connect(m_ui->commandsView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
std::bind(&AdbCommandsWidgetPrivate::onCurrentIndexChanged, this, _1, _2));
|
||||
}
|
||||
|
||||
AdbCommandsWidgetPrivate::~AdbCommandsWidgetPrivate()
|
||||
{
|
||||
delete m_ui;
|
||||
delete m_stringModel;
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::addString(const QString &str)
|
||||
{
|
||||
if (!str.isEmpty()) {
|
||||
m_stringModel->insertRows(m_stringModel->rowCount(), 1);
|
||||
const QModelIndex lastItemIndex = m_stringModel->index(m_stringModel->rowCount() - 1);
|
||||
m_stringModel->setData(lastItemIndex, str);
|
||||
}
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onAddButton()
|
||||
{
|
||||
addString(defaultCommand);
|
||||
const QModelIndex index = m_stringModel->index(m_stringModel->rowCount() - 1);
|
||||
m_ui->commandsView->setCurrentIndex(index);
|
||||
m_ui->commandsView->edit(index);
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onMoveUpButton()
|
||||
{
|
||||
QModelIndex index = m_ui->commandsView->currentIndex();
|
||||
if (index.row() > 0) {
|
||||
const QModelIndex newIndex = m_stringModel->index(index.row() - 1, 0);
|
||||
swapData(m_stringModel, index, newIndex);
|
||||
m_ui->commandsView->setCurrentIndex(newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onMoveDownButton()
|
||||
{
|
||||
QModelIndex index = m_ui->commandsView->currentIndex();
|
||||
if (index.row() < m_stringModel->rowCount() - 1) {
|
||||
const QModelIndex newIndex = m_stringModel->index(index.row() + 1, 0);
|
||||
swapData(m_stringModel, index, newIndex);
|
||||
m_ui->commandsView->setCurrentIndex(newIndex);
|
||||
}
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onRemove()
|
||||
{
|
||||
const QModelIndex &index = m_ui->commandsView->currentIndex();
|
||||
if (index.isValid())
|
||||
m_stringModel->removeRow(index.row());
|
||||
}
|
||||
|
||||
void AdbCommandsWidgetPrivate::onCurrentIndexChanged(const QModelIndex &newIndex, const QModelIndex &prevIndex)
|
||||
{
|
||||
Q_UNUSED(prevIndex)
|
||||
m_ui->moveUpButton->setEnabled(newIndex.row() != 0);
|
||||
m_ui->moveDownButton->setEnabled(newIndex.row() < m_stringModel->rowCount() - 1);
|
||||
m_ui->removeButton->setEnabled(newIndex.isValid());
|
||||
}
|
||||
|
||||
} // Internal
|
||||
} // Android
|
64
src/plugins/android/adbcommandswidget.h
Normal file
64
src/plugins/android/adbcommandswidget.h
Normal file
@@ -0,0 +1,64 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2017 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 <QObject>
|
||||
#include <QStringList>
|
||||
|
||||
#include <memory>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QWidget;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
|
||||
class AdbCommandsWidgetPrivate;
|
||||
|
||||
class AdbCommandsWidget : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AdbCommandsWidget(QWidget *parent);
|
||||
~AdbCommandsWidget();
|
||||
|
||||
QStringList commandsList() const;
|
||||
void setCommandList(const QStringList &commands);
|
||||
|
||||
QWidget *widget() const;
|
||||
|
||||
void setTitleText(const QString &title);
|
||||
|
||||
signals:
|
||||
void commandsChanged();
|
||||
|
||||
private:
|
||||
std::unique_ptr<AdbCommandsWidgetPrivate> d;
|
||||
};
|
||||
|
||||
} // Internal
|
||||
} // Android
|
117
src/plugins/android/adbcommandswidget.ui
Normal file
117
src/plugins/android/adbcommandswidget.ui
Normal file
@@ -0,0 +1,117 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>AdbCommandsWidget</class>
|
||||
<widget class="QGroupBox" name="AdbCommandsWidget">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>682</width>
|
||||
<height>391</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Widget</string>
|
||||
</property>
|
||||
<property name="title">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="2" column="1">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" rowspan="6">
|
||||
<widget class="QListView" name="commandsView">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="defaultDropAction">
|
||||
<enum>Qt::MoveAction</enum>
|
||||
</property>
|
||||
<property name="movement">
|
||||
<enum>QListView::Snap</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1">
|
||||
<widget class="QToolButton" name="moveUpButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::NoArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="1">
|
||||
<widget class="QToolButton" name="addButton">
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="4" column="1">
|
||||
<widget class="QToolButton" name="moveDownButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
<property name="arrowType">
|
||||
<enum>Qt::NoArrow</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<widget class="QToolButton" name="removeButton">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<layoutdefault spacing="6" margin="11"/>
|
||||
<tabstops>
|
||||
<tabstop>commandsView</tabstop>
|
||||
<tabstop>addButton</tabstop>
|
||||
<tabstop>removeButton</tabstop>
|
||||
<tabstop>moveUpButton</tabstop>
|
||||
<tabstop>moveDownButton</tabstop>
|
||||
</tabstops>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
@@ -50,7 +50,8 @@ HEADERS += \
|
||||
androidtoolmanager.h \
|
||||
androidsdkmanager.h \
|
||||
androidavdmanager.h \
|
||||
androidrunconfigurationwidget.h
|
||||
androidrunconfigurationwidget.h \
|
||||
adbcommandswidget.h
|
||||
|
||||
SOURCES += \
|
||||
androidconfigurations.cpp \
|
||||
@@ -94,7 +95,8 @@ SOURCES += \
|
||||
androidtoolmanager.cpp \
|
||||
androidsdkmanager.cpp \
|
||||
androidavdmanager.cpp \
|
||||
androidrunconfigurationwidget.cpp
|
||||
androidrunconfigurationwidget.cpp \
|
||||
adbcommandswidget.cpp
|
||||
|
||||
FORMS += \
|
||||
androidsettingswidget.ui \
|
||||
@@ -103,7 +105,8 @@ FORMS += \
|
||||
androiddevicedialog.ui \
|
||||
androiddeployqtwidget.ui \
|
||||
androidbuildapkwidget.ui \
|
||||
androidrunconfigurationwidget.ui
|
||||
androidrunconfigurationwidget.ui \
|
||||
adbcommandswidget.ui
|
||||
|
||||
RESOURCES = android.qrc
|
||||
|
||||
|
@@ -18,8 +18,11 @@ Project {
|
||||
|
||||
files: [
|
||||
"android_global.h",
|
||||
"addnewavddialog.ui",
|
||||
"android.qrc",
|
||||
"adbcommandswidget.cpp",
|
||||
"adbcommandswidget.h",
|
||||
"adbcommandswidget.ui",
|
||||
"addnewavddialog.ui",
|
||||
"androidanalyzesupport.cpp",
|
||||
"androidanalyzesupport.h",
|
||||
"androidavdmanager.cpp",
|
||||
|
@@ -40,7 +40,10 @@ using namespace ProjectExplorer;
|
||||
|
||||
namespace Android {
|
||||
using namespace Internal;
|
||||
|
||||
const char amStartArgsKey[] = "Android.AmStartArgsKey";
|
||||
const char preStartShellCmdsKey[] = "Android.PreStartShellCmdListKey";
|
||||
const char postFinishShellCmdsKey[] = "Android.PostFinishShellCmdListKey";
|
||||
|
||||
AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, Core::Id id)
|
||||
: RunConfiguration(parent, id)
|
||||
@@ -52,6 +55,16 @@ AndroidRunConfiguration::AndroidRunConfiguration(Target *parent, AndroidRunConfi
|
||||
{
|
||||
}
|
||||
|
||||
void AndroidRunConfiguration::setPreStartShellCommands(const QStringList &cmdList)
|
||||
{
|
||||
m_preStartShellCommands = cmdList;
|
||||
}
|
||||
|
||||
void AndroidRunConfiguration::setPostFinishShellCommands(const QStringList &cmdList)
|
||||
{
|
||||
m_postFinishShellCommands = cmdList;
|
||||
}
|
||||
|
||||
void AndroidRunConfiguration::setAmStartExtraArgs(const QStringList &args)
|
||||
{
|
||||
m_amStartExtraArgs = args;
|
||||
@@ -61,8 +74,14 @@ QWidget *AndroidRunConfiguration::createConfigurationWidget()
|
||||
{
|
||||
auto configWidget = new AndroidRunConfigurationWidget();
|
||||
configWidget->setAmStartArgs(m_amStartExtraArgs);
|
||||
configWidget->setPreStartShellCommands(m_preStartShellCommands);
|
||||
configWidget->setPostFinishShellCommands(m_postFinishShellCommands);
|
||||
connect(configWidget, &AndroidRunConfigurationWidget::amStartArgsChanged,
|
||||
this, &AndroidRunConfiguration::setAmStartExtraArgs);
|
||||
connect(configWidget, &AndroidRunConfigurationWidget::preStartCmdsChanged,
|
||||
this, &AndroidRunConfiguration::setPreStartShellCommands);
|
||||
connect(configWidget, &AndroidRunConfigurationWidget::postFinishCmdsChanged,
|
||||
this, &AndroidRunConfiguration::setPostFinishShellCommands);
|
||||
return configWidget;
|
||||
}
|
||||
|
||||
@@ -73,6 +92,8 @@ Utils::OutputFormatter *AndroidRunConfiguration::createOutputFormatter() const
|
||||
|
||||
bool AndroidRunConfiguration::fromMap(const QVariantMap &map)
|
||||
{
|
||||
m_preStartShellCommands = map.value(preStartShellCmdsKey).toStringList();
|
||||
m_postFinishShellCommands = map.value(postFinishShellCmdsKey).toStringList();
|
||||
m_amStartExtraArgs = map.value(amStartArgsKey).toStringList();
|
||||
return RunConfiguration::fromMap(map);
|
||||
}
|
||||
@@ -80,6 +101,8 @@ bool AndroidRunConfiguration::fromMap(const QVariantMap &map)
|
||||
QVariantMap AndroidRunConfiguration::toMap() const
|
||||
{
|
||||
QVariantMap res = RunConfiguration::toMap();
|
||||
res[preStartShellCmdsKey] = m_preStartShellCommands;
|
||||
res[postFinishShellCmdsKey] = m_postFinishShellCommands;
|
||||
res[amStartArgsKey] = m_amStartExtraArgs;
|
||||
return res;
|
||||
}
|
||||
@@ -88,4 +111,15 @@ const QStringList &AndroidRunConfiguration::amStartExtraArgs() const
|
||||
{
|
||||
return m_amStartExtraArgs;
|
||||
}
|
||||
|
||||
const QStringList &AndroidRunConfiguration::preStartShellCommands() const
|
||||
{
|
||||
return m_preStartShellCommands;
|
||||
}
|
||||
|
||||
const QStringList &AndroidRunConfiguration::postFinishShellCommands() const
|
||||
{
|
||||
return m_postFinishShellCommands;
|
||||
}
|
||||
|
||||
} // namespace Android
|
||||
|
@@ -48,15 +48,21 @@ public:
|
||||
QVariantMap toMap() const override;
|
||||
|
||||
const QStringList &amStartExtraArgs() const;
|
||||
const QStringList &preStartShellCommands() const;
|
||||
const QStringList &postFinishShellCommands() const;
|
||||
|
||||
protected:
|
||||
AndroidRunConfiguration(ProjectExplorer::Target *parent, AndroidRunConfiguration *source);
|
||||
|
||||
private:
|
||||
void setPreStartShellCommands(const QStringList &cmdList);
|
||||
void setPostFinishShellCommands(const QStringList &cmdList);
|
||||
void setAmStartExtraArgs(const QStringList &args);
|
||||
|
||||
private:
|
||||
QStringList m_amStartExtraArgs;
|
||||
QStringList m_preStartShellCommands;
|
||||
QStringList m_postFinishShellCommands;
|
||||
};
|
||||
|
||||
} // namespace Android
|
||||
|
@@ -23,6 +23,7 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "androidrunconfigurationwidget.h"
|
||||
#include "adbcommandswidget.h"
|
||||
#include "ui_androidrunconfigurationwidget.h"
|
||||
|
||||
#include "utils/utilsicons.h"
|
||||
@@ -39,6 +40,26 @@ AndroidRunConfigurationWidget::AndroidRunConfigurationWidget(QWidget *parent):
|
||||
m_ui->setupUi(detailsWidget);
|
||||
m_ui->m_warningIconLabel->setPixmap(Utils::Icons::WARNING.pixmap());
|
||||
|
||||
m_preStartCmdsWidget = new AdbCommandsWidget(detailsWidget);
|
||||
connect(m_preStartCmdsWidget, &AdbCommandsWidget::commandsChanged, [this]() {
|
||||
emit preStartCmdsChanged(m_preStartCmdsWidget->commandsList());
|
||||
});
|
||||
m_preStartCmdsWidget->setTitleText(tr("Shell commands to run on Android device before"
|
||||
" application launch."));
|
||||
|
||||
m_postEndCmdsWidget = new AdbCommandsWidget(detailsWidget);
|
||||
connect(m_postEndCmdsWidget, &AdbCommandsWidget::commandsChanged, [this]() {
|
||||
emit postFinishCmdsChanged(m_postEndCmdsWidget->commandsList());
|
||||
});
|
||||
m_postEndCmdsWidget->setTitleText(tr("Shell commands to run on Android device after application"
|
||||
" quits."));
|
||||
|
||||
auto mainLayout = static_cast<QGridLayout*>(detailsWidget->layout());
|
||||
mainLayout->addWidget(m_preStartCmdsWidget->widget(), mainLayout->rowCount(),
|
||||
0, mainLayout->columnCount() - 1, 0);
|
||||
mainLayout->addWidget(m_postEndCmdsWidget->widget(), mainLayout->rowCount(),
|
||||
0, mainLayout->columnCount() - 1, 0);
|
||||
|
||||
setWidget(detailsWidget);
|
||||
setSummaryText(tr("Android run settings"));
|
||||
|
||||
@@ -54,10 +75,19 @@ AndroidRunConfigurationWidget::~AndroidRunConfigurationWidget()
|
||||
|
||||
void AndroidRunConfigurationWidget::setAmStartArgs(const QStringList &args)
|
||||
{
|
||||
if (m_ui->m_amStartArgsEdit && !args.isEmpty())
|
||||
m_ui->m_amStartArgsEdit->setText(Utils::QtcProcess::joinArgs(args, Utils::OsTypeLinux));
|
||||
}
|
||||
|
||||
void AndroidRunConfigurationWidget::setPreStartShellCommands(const QStringList &cmdList)
|
||||
{
|
||||
m_preStartCmdsWidget->setCommandList(cmdList);
|
||||
}
|
||||
|
||||
void AndroidRunConfigurationWidget::setPostFinishShellCommands(const QStringList &cmdList)
|
||||
{
|
||||
m_postEndCmdsWidget->setCommandList(cmdList);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Android
|
||||
|
||||
|
@@ -29,10 +29,14 @@
|
||||
#include "projectexplorer/runconfiguration.h"
|
||||
#include "utils/detailswidget.h"
|
||||
|
||||
#include <QStringListModel>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Android {
|
||||
namespace Internal {
|
||||
|
||||
class AdbCommandsWidget;
|
||||
namespace Ui {
|
||||
class AndroidRunConfigurationWidget;
|
||||
}
|
||||
@@ -45,12 +49,22 @@ public:
|
||||
~AndroidRunConfigurationWidget();
|
||||
|
||||
void setAmStartArgs(const QStringList &args);
|
||||
void setPreStartShellCommands(const QStringList &cmdList);
|
||||
void setPostFinishShellCommands(const QStringList &cmdList);
|
||||
|
||||
signals:
|
||||
void amStartArgsChanged(QStringList args);
|
||||
void preStartCmdsChanged(const QStringList &cmdList);
|
||||
void postFinishCmdsChanged(const QStringList &cmdList);
|
||||
|
||||
private:
|
||||
void addPreStartCommand(const QString &command);
|
||||
void addPostFinishCommand(const QString &command);
|
||||
|
||||
private:
|
||||
std::unique_ptr<Ui::AndroidRunConfigurationWidget> m_ui;
|
||||
AdbCommandsWidget *m_preStartCmdsWidget = nullptr;
|
||||
AdbCommandsWidget *m_postEndCmdsWidget = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
@@ -6,25 +6,31 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>625</width>
|
||||
<height>73</height>
|
||||
<width>731</width>
|
||||
<height>119</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="2">
|
||||
<widget class="QLineEdit" name="m_amStartArgsEdit"/>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="spacing">
|
||||
<number>-1</number>
|
||||
</property>
|
||||
<item row="1" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Conflicting "am start" options might result in the app startup failure.</string>
|
||||
<string>Activity manager start options:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1">
|
||||
<item row="2" column="1">
|
||||
<widget class="QLabel" name="m_warningIconLabel">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Maximum" vsizetype="Preferred">
|
||||
@@ -37,7 +43,7 @@
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="2" column="0">
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
@@ -53,13 +59,32 @@
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="0" column="0" colspan="2">
|
||||
<widget class="QLabel" name="label">
|
||||
<item row="3" column="0" colspan="3">
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeType">
|
||||
<enum>QSizePolicy::Fixed</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>12</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="2" column="2">
|
||||
<widget class="QLabel" name="label_3">
|
||||
<property name="text">
|
||||
<string>Activity manager start options:</string>
|
||||
<string>Conflicting "am start" options might result in the app startup failure.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="2">
|
||||
<widget class="QLineEdit" name="m_amStartArgsEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
|
@@ -37,8 +37,8 @@ struct ANDROID_EXPORT AndroidRunnable
|
||||
QString intentName;
|
||||
QStringList amStartExtraArgs;
|
||||
Utils::Environment environment;
|
||||
QVector<QStringList> beforeStartADBCommands;
|
||||
QVector<QStringList> afterFinishADBCommands;
|
||||
QStringList beforeStartAdbCommands;
|
||||
QStringList afterFinishAdbCommands;
|
||||
QString deviceSerialNumber;
|
||||
|
||||
QString displayName() const { return packageName; }
|
||||
@@ -51,8 +51,8 @@ inline bool operator==(const AndroidRunnable &r1, const AndroidRunnable &r2)
|
||||
&& r1.intentName == r2.intentName
|
||||
&& r1.amStartExtraArgs == r2.amStartExtraArgs
|
||||
&& r1.environment == r2.environment
|
||||
&& r1.beforeStartADBCommands == r2.beforeStartADBCommands
|
||||
&& r1.afterFinishADBCommands == r2.afterFinishADBCommands
|
||||
&& r1.beforeStartAdbCommands == r2.beforeStartAdbCommands
|
||||
&& r1.afterFinishAdbCommands == r2.afterFinishAdbCommands
|
||||
&& r1.deviceSerialNumber == r2.deviceSerialNumber;
|
||||
}
|
||||
|
||||
|
@@ -214,14 +214,13 @@ class AndroidRunnerWorker : public QObject
|
||||
};
|
||||
|
||||
public:
|
||||
AndroidRunnerWorker(RunControl *runControl, const QString &packageName,
|
||||
const QStringList &selector);
|
||||
AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable);
|
||||
~AndroidRunnerWorker();
|
||||
|
||||
void asyncStart(const AndroidRunnable &runnable);
|
||||
void asyncStop(const AndroidRunnable &runnable);
|
||||
void asyncStart();
|
||||
void asyncStop();
|
||||
|
||||
void setAdbParameters(const QString &packageName, const QStringList &selector);
|
||||
void setAndroidRunnable(const AndroidRunnable &runnable);
|
||||
void handleRemoteDebuggerRunning();
|
||||
|
||||
Utils::Port localGdbServerPort() const { return m_localGdbServerPort; }
|
||||
@@ -238,7 +237,7 @@ private:
|
||||
void logcatReadStandardError();
|
||||
void logcatReadStandardOutput();
|
||||
void adbKill(qint64 pid);
|
||||
QStringList selector() const { return m_selector; }
|
||||
QStringList selector() const;
|
||||
void forceStop();
|
||||
void findPs();
|
||||
void logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError);
|
||||
@@ -264,22 +263,19 @@ private:
|
||||
QString m_gdbserverPath;
|
||||
QString m_gdbserverSocket;
|
||||
QString m_adb;
|
||||
QStringList m_selector;
|
||||
QRegExp m_logCatRegExp;
|
||||
DebugHandShakeType m_handShakeMethod = SocketHandShake;
|
||||
bool m_customPort = false;
|
||||
|
||||
QString m_packageName;
|
||||
AndroidRunnable m_androidRunnable;
|
||||
int m_socketHandShakePort = MIN_SOCKET_HANDSHAKE_PORT;
|
||||
};
|
||||
|
||||
AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const QString &packageName,
|
||||
const QStringList &selector)
|
||||
AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const AndroidRunnable &runnable)
|
||||
: m_adbLogcatProcess(nullptr, deleter)
|
||||
, m_psIsAlive(nullptr, deleter)
|
||||
, m_selector(selector)
|
||||
, m_logCatRegExp(regExpLogcat)
|
||||
, m_packageName(packageName)
|
||||
, m_androidRunnable(runnable)
|
||||
{
|
||||
auto runConfig = runControl->runConfiguration();
|
||||
auto aspect = runConfig->extraAspect<Debugger::DebuggerRunConfigurationAspect>();
|
||||
@@ -307,9 +303,9 @@ AndroidRunnerWorker::AndroidRunnerWorker(RunControl *runControl, const QString &
|
||||
}
|
||||
m_adb = AndroidConfigurations::currentConfig().adbToolPath().toString();
|
||||
|
||||
QString packageDir = "/data/data/" + m_packageName;
|
||||
QString packageDir = "/data/data/" + m_androidRunnable.packageName;
|
||||
m_pingFile = packageDir + "/debug-ping";
|
||||
m_pongFile = "/data/local/tmp/qt/debug-pong-" + m_packageName;
|
||||
m_pongFile = "/data/local/tmp/qt/debug-pong-" + m_androidRunnable.packageName;
|
||||
m_gdbserverSocket = packageDir + "/debug-socket";
|
||||
const QtSupport::BaseQtVersion *version = QtSupport::QtKitInformation::qtVersion(
|
||||
runConfig->target()->kit());
|
||||
@@ -347,20 +343,20 @@ AndroidRunnerWorker::~AndroidRunnerWorker()
|
||||
|
||||
void AndroidRunnerWorker::forceStop()
|
||||
{
|
||||
runAdb({"shell", "am", "force-stop", m_packageName}, nullptr, 30);
|
||||
runAdb({"shell", "am", "force-stop", m_androidRunnable.packageName}, nullptr, 30);
|
||||
|
||||
// try killing it via kill -9
|
||||
const QByteArray out = Utils::SynchronousProcess()
|
||||
.runBlocking(m_adb, selector() << QStringLiteral("shell") << pidScript)
|
||||
.allRawOutput();
|
||||
|
||||
qint64 pid = extractPID(out.simplified(), m_packageName);
|
||||
qint64 pid = extractPID(out.simplified(), m_androidRunnable.packageName);
|
||||
if (pid != -1) {
|
||||
adbKill(pid);
|
||||
}
|
||||
}
|
||||
|
||||
void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
void AndroidRunnerWorker::asyncStart()
|
||||
{
|
||||
forceStop();
|
||||
|
||||
@@ -378,12 +374,12 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
if (m_useCppDebugger)
|
||||
runAdb({"shell", "rm", m_pongFile}); // Remove pong file.
|
||||
|
||||
for (const QStringList &entry: runnable.beforeStartADBCommands)
|
||||
runAdb(entry);
|
||||
for (const QString &entry: m_androidRunnable.beforeStartAdbCommands)
|
||||
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
||||
|
||||
QStringList args({"shell", "am", "start"});
|
||||
args << runnable.amStartExtraArgs;
|
||||
args << "-n" << runnable.intentName;
|
||||
args << m_androidRunnable.amStartExtraArgs;
|
||||
args << "-n" << m_androidRunnable.intentName;
|
||||
|
||||
if (m_useCppDebugger) {
|
||||
if (!runAdb({"forward", "--remove", "tcp:" + m_localGdbServerPort.toString()})){
|
||||
@@ -395,7 +391,7 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
return;
|
||||
}
|
||||
|
||||
const QString pingPongSocket(m_packageName + ".ping_pong_socket");
|
||||
const QString pingPongSocket(m_androidRunnable.packageName + ".ping_pong_socket");
|
||||
args << "-e" << "debug_ping" << "true";
|
||||
if (m_handShakeMethod == SocketHandShake) {
|
||||
args << "-e" << "ping_socket" << pingPongSocket;
|
||||
@@ -499,7 +495,8 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
break;
|
||||
|
||||
if (i == 20) {
|
||||
emit remoteProcessFinished(tr("Unable to start \"%1\".").arg(m_packageName));
|
||||
emit remoteProcessFinished(tr("Unable to start \"%1\".")
|
||||
.arg(m_androidRunnable.packageName));
|
||||
return;
|
||||
}
|
||||
qDebug() << "WAITING FOR " << tmp.fileName();
|
||||
@@ -512,7 +509,7 @@ void AndroidRunnerWorker::asyncStart(const AndroidRunnable &runnable)
|
||||
QTC_ASSERT(!m_adbLogcatProcess, /**/);
|
||||
m_adbLogcatProcess = std::move(logcatProcess);
|
||||
m_pidFinder = Utils::onResultReady(Utils::runAsync(&findProcessPID, m_adb, selector(),
|
||||
m_packageName),
|
||||
m_androidRunnable.packageName),
|
||||
bind(&AndroidRunnerWorker::onProcessIdChanged, this, _1));
|
||||
|
||||
}
|
||||
@@ -543,7 +540,7 @@ bool AndroidRunnerWorker::runAdb(const QStringList &args, QString *exitMessage,
|
||||
{
|
||||
Utils::SynchronousProcess adb;
|
||||
adb.setTimeoutS(timeoutS);
|
||||
Utils::SynchronousProcessResponse response = adb.run(m_adb, m_selector + args);
|
||||
Utils::SynchronousProcessResponse response = adb.run(m_adb, selector() + args);
|
||||
if (exitMessage)
|
||||
*exitMessage = response.exitMessage(m_adb, timeoutS);
|
||||
return response.result == Utils::SynchronousProcessResponse::Finished;
|
||||
@@ -567,7 +564,7 @@ void AndroidRunnerWorker::handleRemoteDebuggerRunning()
|
||||
// emit remoteProcessStarted(m_localGdbServerPort, m_qmlPort);
|
||||
}
|
||||
|
||||
void AndroidRunnerWorker::asyncStop(const AndroidRunnable &runnable)
|
||||
void AndroidRunnerWorker::asyncStop()
|
||||
{
|
||||
if (!m_pidFinder.isFinished())
|
||||
m_pidFinder.cancel();
|
||||
@@ -575,14 +572,11 @@ void AndroidRunnerWorker::asyncStop(const AndroidRunnable &runnable)
|
||||
if (m_processPID != -1) {
|
||||
forceStop();
|
||||
}
|
||||
for (const QStringList &entry: runnable.afterFinishADBCommands)
|
||||
runAdb(entry);
|
||||
}
|
||||
|
||||
void AndroidRunnerWorker::setAdbParameters(const QString &packageName, const QStringList &selector)
|
||||
void AndroidRunnerWorker::setAndroidRunnable(const AndroidRunnable &runnable)
|
||||
{
|
||||
m_packageName = packageName;
|
||||
m_selector = selector;
|
||||
m_androidRunnable = runnable;
|
||||
}
|
||||
|
||||
void AndroidRunnerWorker::logcatProcess(const QByteArray &text, QByteArray &buffer, bool onlyError)
|
||||
@@ -635,10 +629,14 @@ void AndroidRunnerWorker::onProcessIdChanged(qint64 pid)
|
||||
m_processPID = pid;
|
||||
if (pid == -1) {
|
||||
emit remoteProcessFinished(QLatin1String("\n\n") + tr("\"%1\" died.")
|
||||
.arg(m_packageName));
|
||||
.arg(m_androidRunnable.packageName));
|
||||
// App died/killed. Reset log and monitor processes.
|
||||
m_adbLogcatProcess.reset();
|
||||
m_psIsAlive.reset();
|
||||
|
||||
// Run adb commands after application quit.
|
||||
for (const QString &entry: m_androidRunnable.afterFinishAdbCommands)
|
||||
runAdb(entry.split(' ', QString::SkipEmptyParts));
|
||||
} else {
|
||||
// In debugging cases this will be funneled to the engine to actually start
|
||||
// and attach gdb. Afterwards this ends up in handleRemoteDebuggerRunning() below.
|
||||
@@ -671,7 +669,12 @@ void AndroidRunnerWorker::logcatReadStandardOutput()
|
||||
void AndroidRunnerWorker::adbKill(qint64 pid)
|
||||
{
|
||||
runAdb({"shell", "kill", "-9", QString::number(pid)});
|
||||
runAdb({"shell", "run-as", m_packageName, "kill", "-9", QString::number(pid)});
|
||||
runAdb({"shell", "run-as", m_androidRunnable.packageName, "kill", "-9", QString::number(pid)});
|
||||
}
|
||||
|
||||
QStringList AndroidRunnerWorker::selector() const
|
||||
{
|
||||
return AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber);
|
||||
}
|
||||
|
||||
AndroidRunner::AndroidRunner(RunControl *runControl)
|
||||
@@ -694,15 +697,19 @@ AndroidRunner::AndroidRunner(RunControl *runControl)
|
||||
|
||||
auto androidRunConfig = qobject_cast<AndroidRunConfiguration *>(runControl->runConfiguration());
|
||||
m_androidRunnable.amStartExtraArgs = androidRunConfig->amStartExtraArgs();
|
||||
for (QString shellCmd: androidRunConfig->preStartShellCommands())
|
||||
m_androidRunnable.beforeStartAdbCommands.append(QString("shell %1").arg(shellCmd));
|
||||
|
||||
m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable.packageName,
|
||||
AndroidDeviceInfo::adbSelector(m_androidRunnable.deviceSerialNumber)));
|
||||
for (QString shellCmd: androidRunConfig->postFinishShellCommands())
|
||||
m_androidRunnable.afterFinishAdbCommands.append(QString("shell %1").arg(shellCmd));
|
||||
|
||||
m_worker.reset(new AndroidRunnerWorker(runControl, m_androidRunnable));
|
||||
m_worker->moveToThread(&m_thread);
|
||||
|
||||
connect(this, &AndroidRunner::asyncStart, m_worker.data(), &AndroidRunnerWorker::asyncStart);
|
||||
connect(this, &AndroidRunner::asyncStop, m_worker.data(), &AndroidRunnerWorker::asyncStop);
|
||||
connect(this, &AndroidRunner::adbParametersChanged,
|
||||
m_worker.data(), &AndroidRunnerWorker::setAdbParameters);
|
||||
connect(this, &AndroidRunner::androidRunnableChanged,
|
||||
m_worker.data(), &AndroidRunnerWorker::setAndroidRunnable);
|
||||
connect(this, &AndroidRunner::remoteDebuggerRunning,
|
||||
m_worker.data(), &AndroidRunnerWorker::handleRemoteDebuggerRunning);
|
||||
|
||||
@@ -738,7 +745,7 @@ void AndroidRunner::start()
|
||||
}
|
||||
}
|
||||
|
||||
emit asyncStart(m_androidRunnable);
|
||||
emit asyncStart();
|
||||
}
|
||||
|
||||
void AndroidRunner::stop()
|
||||
@@ -750,7 +757,7 @@ void AndroidRunner::stop()
|
||||
return;
|
||||
}
|
||||
|
||||
emit asyncStop(m_androidRunnable);
|
||||
emit asyncStop();
|
||||
}
|
||||
|
||||
void AndroidRunner::qmlServerPortReady(Port port)
|
||||
@@ -797,8 +804,7 @@ void AndroidRunner::setRunnable(const AndroidRunnable &runnable)
|
||||
{
|
||||
if (runnable != m_androidRunnable) {
|
||||
m_androidRunnable = runnable;
|
||||
emit adbParametersChanged(runnable.packageName,
|
||||
AndroidDeviceInfo::adbSelector(runnable.deviceSerialNumber));
|
||||
emit androidRunnableChanged(m_androidRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -816,8 +822,7 @@ void AndroidRunner::launchAVD()
|
||||
AndroidConfigurations::None);
|
||||
AndroidManager::setDeviceSerialNumber(m_target, info.serialNumber);
|
||||
m_androidRunnable.deviceSerialNumber = info.serialNumber;
|
||||
emit adbParametersChanged(m_androidRunnable.packageName,
|
||||
AndroidDeviceInfo::adbSelector(info.serialNumber));
|
||||
emit androidRunnableChanged(m_androidRunnable);
|
||||
if (info.isValid()) {
|
||||
AndroidAvdManager avdManager;
|
||||
if (avdManager.findAvd(info.avdname).isEmpty()) {
|
||||
@@ -840,7 +845,7 @@ void AndroidRunner::checkAVD()
|
||||
if (avdManager.isAvdBooted(serialNumber)) {
|
||||
m_checkAVDTimer.stop();
|
||||
AndroidManager::setDeviceSerialNumber(m_target, serialNumber);
|
||||
emit asyncStart(m_androidRunnable);
|
||||
emit asyncStart();
|
||||
} else if (!config.isConnected(serialNumber)) {
|
||||
// device was disconnected
|
||||
m_checkAVDTimer.stop();
|
||||
|
@@ -64,12 +64,11 @@ public:
|
||||
void stop() override;
|
||||
|
||||
signals:
|
||||
void asyncStart(const AndroidRunnable &runnable);
|
||||
void asyncStop(const AndroidRunnable &runnable);
|
||||
void asyncStart();
|
||||
void asyncStop();
|
||||
void remoteDebuggerRunning();
|
||||
void qmlServerReady(const QUrl &serverUrl);
|
||||
|
||||
void adbParametersChanged(const QString &packageName, const QStringList &selector);
|
||||
void androidRunnableChanged(const AndroidRunnable &runnable);
|
||||
void avdDetected();
|
||||
|
||||
private:
|
||||
|
Reference in New Issue
Block a user